{"id":14958186,"url":"https://github.com/logancyang/loss-landscape-anim","last_synced_at":"2025-04-09T15:06:14.077Z","repository":{"id":54993985,"uuid":"326814526","full_name":"logancyang/loss-landscape-anim","owner":"logancyang","description":"Create animations for the optimization trajectory of neural nets","archived":false,"fork":false,"pushed_at":"2024-01-30T05:58:57.000Z","size":75035,"stargazers_count":149,"open_issues_count":3,"forks_count":24,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-09T15:06:02.038Z","etag":null,"topics":["loss-landscape","neural-nets","optimization-trajectory","pca","pytorch","pytorch-lightning"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/logancyang.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-01-04T21:42:13.000Z","updated_at":"2025-04-01T11:41:39.000Z","dependencies_parsed_at":"2024-09-24T13:35:33.069Z","dependency_job_id":null,"html_url":"https://github.com/logancyang/loss-landscape-anim","commit_stats":{"total_commits":60,"total_committers":3,"mean_commits":20.0,"dds":0.06666666666666665,"last_synced_commit":"a8d5610ff433b94aca68af42952f0bad8e0e34b3"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logancyang%2Floss-landscape-anim","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logancyang%2Floss-landscape-anim/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logancyang%2Floss-landscape-anim/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/logancyang%2Floss-landscape-anim/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/logancyang","download_url":"https://codeload.github.com/logancyang/loss-landscape-anim/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248055284,"owners_count":21040157,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["loss-landscape","neural-nets","optimization-trajectory","pca","pytorch","pytorch-lightning"],"created_at":"2024-09-24T13:16:26.349Z","updated_at":"2025-04-09T15:06:14.056Z","avatar_url":"https://github.com/logancyang.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Animating the Optimization Trajectory of Neural Nets\n\n[![PyPi Latest Release](https://img.shields.io/pypi/v/loss-landscape-anim)](https://pypi.org/project/loss-landscape-anim/)\n[![Release](https://img.shields.io/github/v/release/logancyang/loss-landscape-anim.svg)](https://github.com/logancyang/loss-landscape-anim/releases)\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)\n\n`loss-landscape-anim` lets you create animated optimization path in a 2D slice of the loss landscape of your neural networks. It is based on [PyTorch Lightning](https://github.com/PyTorchLightning/pytorch-lightning), please follow its suggested style if you want to add your own model.\n\nCheck out my article [Visualizing Optimization Trajectory of Neural Nets](https://towardsdatascience.com/from-animation-to-intuition-visualizing-optimization-trajectory-in-neural-nets-726e43a08d85?sk=dae85760fb921ecacddbe1af903e3c69) for more examples and some intuitive explanations.\n\n## 0. Installation\n\nFrom PyPI:\n\n```sh\npip install loss-landscape-anim\n```\n\nFrom source, you need [Poetry](https://python-poetry.org/docs/#installation). Once you cloned this repo, run the command below to install the dependencies.\n\n```sh\npoetry install\n```\n\n## 1. Basic Examples\n\nWith the provided [spirals dataset](https://github.com/logancyang/loss-landscape-anim/blob/master/sample_images/spirals-dataset.png) and the default multilayer perceptron `MLP` model, you can directly call `loss_landscape_anim` to get a sample animated GIF like this:\n\n```py\n# Use default MLP model and sample spirals dataset\nloss_landscape_anim(n_epochs=300)\n```\n\n\u003cimg src=\"./sample_images/sample_mlp_2l_50n.gif\" alt=\"sample gif 1\" title=\"MLP with two 50-node hidden layers on the Spirals dataset, PCA\" align=\"middle\"/\u003e\n\nNote: if you are using it in a notebook, don't forget to include the following at the top:\n\n```py\n%matplotlib notebook\n```\n\nHere's another example – the LeNet5 convolutional network on the MNIST dataset. There are many levers you can tune: learning rate, batch size, epochs, frames per second of the GIF output, a seed for reproducible results, whether to load from a trained model, etc. Check out the function signature for more details.\n\n```py\nbs = 16\nlr = 1e-3\ndatamodule = MNISTDataModule(batch_size=bs, n_examples=3000)\nmodel = LeNet(learning_rate=lr)\n\noptim_path, loss_steps, accu_steps = loss_landscape_anim(\n    n_epochs=10,\n    model=model,\n    datamodule=datamodule,\n    optimizer=\"adam\",\n    giffps=15,\n    seed=SEED,\n    load_model=False,\n    output_to_file=True,\n    return_data=True,  # Optional return values if you need them\n    gpus=1  # Enable GPU training if available\n)\n```\n\nGPU training is supported. Just pass `gpus` into `loss_landscape_anim` if they are available.\n\nThe output of LeNet5 on the MNIST dataset looks like this:\n\n\u003cimg src=\"./sample_images/lenet-1e-3.gif\" alt=\"sample gif 2\" title=\"LeNet5 on the MNIST dataset, PCA\" align=\"middle\"/\u003e\n\n## 2. Why PCA?\n\nTo create a 2D visualization, the first thing to do is to pick the 2 directions that define the plane. In the paper [Visualizing the Loss Landscape of Neural Nets](https://arxiv.org/abs/1712.09913v3), the authors argued why 2 random directions don't work and why PCA is much better. In summary,\n\n1) 2 random vectors in high dimensional space have a high probability of being orthogonal, and they can hardly capture any variation for the optimization path. The path’s projection onto the plane spanned by the 2 vectors will just look like random walk.\n\n2) If we pick one direction to be the vector pointing from the initial parameters to the final trained parameters, and another direction at random, the visualization will look like a straight line because the second direction doesn’t capture much variance compared to the first.\n\n3) If we use principal component analysis (PCA) on the optimization path and get the top 2 components, we can visualize the loss over the 2 orthogonal directions with the most variance.\n\nFor showing *the most motion in 2D*, PCA is preferred. If you need a quick recap on PCA, here's a [minimal example](https://towardsdatascience.com/a-3-minute-review-of-pca-compression-and-recovery-38bb510a8637?sk=028aee2c8b0f3cf8b0207563a3ff907d) you can go over under 3 minutes.\n\n\n## 3. Random and Custom Directions\n\nAlthough PCA is a good approach for picking the directions, if you need more control, the code also allows you to set any 2 fixed directions, either generated at random or handpicked.\n\nFor 2 random directions, set `reduction_method` to `\"random\"`, e.g.\n\n```py\nloss_landscape_anim(n_epochs=300, load_model=False, reduction_method=\"random\")\n```\n\nFor 2 fixed directions of your choosing, set `reduction_method` to `\"custom\"`, e.g.\n\n```py\nimport numpy as np\n\nn_params = ... # number of parameters your model has\nu_gen = np.random.normal(size=n_params)\nu = u_gen / np.linalg.norm(u_gen)\nv_gen = np.random.normal(size=n_params)\nv = v_gen / np.linalg.norm(v_gen)\n\nloss_landscape_anim(\n    n_epochs=300, load_model=False, reduction_method=\"custom\", custom_directions=(u, v)\n)\n```\n\nHere is an sample GIF produced by two random directions:\n\n\u003cimg src=\"./sample_images/random_directions.gif\" alt=\"sample gif 3\" title=\"MLP with 1 50-node hidden layer on the Spirals dataset, random directions\" align=\"middle\"/\u003e\n\nBy default, `reduction_method=\"pca\"`.\n\n## 4. Custom Dataset and Model\n\n1. Prepare your `DataModule`. Refer to [datamodule.py](https://github.com/logancyang/loss-landscape-anim/blob/master/loss_landscape_anim/datamodule.py) for examples.\n2. Define your custom model that inherits `model.GenericModel`. Refer to [model.py](https://github.com/logancyang/loss-landscape-anim/blob/master/loss_landscape_anim/model.py) for examples.\n3. Once you correctly setup your custom `DataModule` and `model`, call the function as shown below to train the model and plot the loss landscape animation.\n\n```py\nbs = ...\nlr = ...\ndatamodule = YourDataModule(batch_size=bs)\nmodel = YourModel(learning_rate=lr)\n\nloss_landscape_anim(\n    n_epochs=10,\n    model=model,\n    datamodule=datamodule,\n    optimizer=\"adam\",\n    seed=SEED,\n    load_model=False,\n    output_to_file=True\n)\n```\n\n## 5. Comparing Different Optimizers\n\nAs mentioned in section 2, the optimization path usually falls into a very low-dimensional space, and its projection in other directions may look like random walk. On the other hand, different optimizers can take very different paths in the high dimensional space. As a result, it is difficult to pick 2 directions to effectively compare different optimizers.\n\nIn this example, I have `adam, sgd, adagrad, rmsprop` initialized with the same parameters. The two figures below share the same 2 random directions but are centered around different local minima. The first figure centers around the one Adam finds, the second centers around the one RMSprop finds. Essentially, the planes are 2 parallel slices of the loss landscape.\n\nThe first figure shows that when centering on the end of Adam's path, it looks like RMSprop is going somewhere with larger loss value. But **that is an illusion**. If you inspect the loss values of RMSprop, it actually finds a local optimum that has a lower loss than Adam's.\n\n*Same 2 directions centering on Adam's path:*\n\n\u003cimg src=\"./sample_images/adam_paths.gif\" alt=\"adam\" title=\"Fixed directions centering on Adam's path\" align=\"middle\"/\u003e\n\n*Same 2 directions centering on RMSprop's path:*\n\n\u003cimg src=\"./sample_images/rmsprop_paths.gif\" alt=\"rmsprop\" title=\"Fixed directions centering on RMSprop's path\" align=\"middle\"/\u003e\n\n**This is a good reminder that the contours are just a 2D slice out of a very high-dimensional loss landscape, and the projections can't reflect the actual path.**\n\nHowever, we can see that the contours are convex no matter where it centers around in these 2 special cases. It more or less reflects that the optimizers shouldn't have a hard time finding a relatively good local minimum. To measure convexity more rigorously, the paper \u003csup\u003e[[1]](#reference)\u003c/sup\u003e mentioned a better method – using *principal curvature*, i.e. the eigenvalues of the Hessian. Check out the end of section 6 in the paper for more details.\n\n## Reference\n\n[1] [Visualizing the Loss Landscape of Neural Nets](https://arxiv.org/abs/1712.09913v3)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flogancyang%2Floss-landscape-anim","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flogancyang%2Floss-landscape-anim","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flogancyang%2Floss-landscape-anim/lists"}