{"id":31762279,"url":"https://github.com/hoom4n/bachnet","last_synced_at":"2026-05-08T07:32:53.012Z","repository":{"id":318696791,"uuid":"1072037058","full_name":"Hoom4n/BachNet","owner":"Hoom4n","description":"A Generative-AI system for Bach-Style music composition using multi-layer LSTM neural networks. Features production-ready deployment (easy inference, modular design, Docker), advanced training recipes for stability and quality, and an efficient streaming data pipeline.","archived":false,"fork":false,"pushed_at":"2025-10-08T16:33:30.000Z","size":61788,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-08T18:42:35.107Z","etag":null,"topics":["autoregressive-models","deep-learning","docker","generative-ai","keras","lstm-neural-networks","machine-learning","music-generation","python","tensorflow"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Hoom4n.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-08T07:08:27.000Z","updated_at":"2025-10-08T16:33:33.000Z","dependencies_parsed_at":null,"dependency_job_id":"e38f5651-876b-4965-8cf6-02d93268433f","html_url":"https://github.com/Hoom4n/BachNet","commit_stats":null,"previous_names":["hoom4n/bachnet"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/Hoom4n/BachNet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hoom4n%2FBachNet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hoom4n%2FBachNet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hoom4n%2FBachNet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hoom4n%2FBachNet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Hoom4n","download_url":"https://codeload.github.com/Hoom4n/BachNet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Hoom4n%2FBachNet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002122,"owners_count":26083307,"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","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["autoregressive-models","deep-learning","docker","generative-ai","keras","lstm-neural-networks","machine-learning","music-generation","python","tensorflow"],"created_at":"2025-10-09T22:17:41.583Z","updated_at":"2026-05-08T07:32:52.997Z","avatar_url":"https://github.com/Hoom4n.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n## 🎵 BachNet: AI-Generated Bach-Style Music 🎼\n![Music-Generation](https://img.shields.io/badge/Music-Generation-FF4081?logo=musicbrainz)\n![Python](https://img.shields.io/badge/Python-3.12-3776AB?logo=python)\n![TensorFlow](https://img.shields.io/badge/TensorFlow-2.19.0-FF6F00?logo=tensorflow)\n![Keras](https://img.shields.io/badge/Keras-3.10-D00000?logo=keras)\n![Docker](https://img.shields.io/badge/Docker-Ready-2496ED?logo=docker)\n![Gradio](https://img.shields.io/badge/Gradio-Interface-FF6F20?logo=gradio)\n\n\n\nBachNet is a production‑ready Deep Learning system for generating music in the style of J.S. Bach. Trained on a corpus of 382 chorales with a multi‑layer, sequence‑to‑sequence LSTM network, it learns both melodic patterns and temporal structures from sequences of 256 notes.\n\nFrom a short seed segment of a chorale, BachNet can autoregressively compose entirely new pieces. Notes are sampled from a categorical distribution, with the degree of variation controlled by a temperature parameter.\n\nThe architecture integrates embeddings, stacked LSTM building blocks with dropout and normalization, and optional dense layers for richer representations. Training is guided by a custom perplexity metric, adaptive learning‑rate scheduling, and early stopping, ensuring stable convergence and interpretable evaluation. The project also incorporates a fully in‑graph TensorFlow data‑streaming pipeline, enabling efficient, on‑the‑fly creation and batching of training samples.\n\n🌐 Try Online Demo on HuggingFace 🤗: \u003ca href=\"https://hoom4n-bachnet.hf.space/\"\u003ehttps://hoom4n-bachnet.hf.space/\u003c/a\u003e\u003cbr/\u003e\u003cbr/\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/main_scr.png\" width=\"80%\" alt=\"Screenshot\"\u003e\n\u003c/p\u003e\n\n## ⚙️ Installation\n### 🐳 Docker\n```bash\n# Clone repo\ngit clone https://github.com/hoom4n/BachNet.git\ncd BachNet\n\n# Build image\ndocker compose up --build\n\n# Later runs\ndocker compose up\n```\n### 💻 Local\n```bash\n# Clone repo\ngit clone https://github.com/hoom4n/BachNet.git\ncd BachNet\n\n# Create \u0026 activate venv\npython3 -m venv bachnet\nsource bachnet/bin/activate\n\n# Install Python deps\npip install -r requirements.txt\n\n# Install FluidSynth (Ubuntu)\nsudo apt-get update \u0026\u0026 sudo apt-get install -y fluidsynth\n\n# Run app\npython app.py\n```\n\n## 🧩 Technical Details\n\n### 📂 Dataset:\nThe system is trained on 382 J.S. Bach chorales, each represented as sequences of four‑voice chords. Across the corpus there are 46 unique notes plus a silence token (47 symbols total). Each chorale is flattened into a 1‑D token sequence, from which overlapping windows of 256 notes are extracted. This window size enables the model to capture long‑range melodic and harmonic dependencies. The target for training is simply the input sequence shifted by one step, yielding about 260,000 input–target pairs in total.\n\nA fully in‑graph TensorFlow data pipeline handles preprocessing and streaming. Chorales are read from disk, tokenized, segmented into windows, and encoded into integer IDs. Batches of 256 samples are then created, with prefetching and caching ensuring that the CPU continuously prepares data while the GPU remains fully utilized. This design minimizes I/O overhead and maximizes throughput during training.\n\n\n\n## 🏗️ Model Architecture\n\nThe network begins with an embedding layer of size 128, which maps each of the 47 unique note tokens (including silence) into a dense, continuous representation. This is followed by a stack of three LSTM blocks, each with 512 hidden units, input dropout, and layer normalization. LSTMs were chosen over GRUs for their additional gating mechanisms, which are better suited to modeling the long‑term melodic and harmonic dependencies characteristic of Bach’s style. Layer normalization further stabilizes training by controlling gradient and activation variance across time steps.\n\nOn top of the recurrent stack, a 256‑unit ReLU feed‑forward layer projects the learned temporal features into a richer latent space, improving the model’s ability to capture nonlinear harmonic relationships. Finally, a dense output layer produces logits over the 47‑token vocabulary, enabling categorical sampling during generation.\n\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"images/bachnet_arch.PNG\" width=\"65%\" alt=\"Screenshot\"\u003e\n\u003c/p\u003e \u003cbr/\u003e\n\n### 📈 Training Setup\n\n| Component        | Details                                                                 |\n|------------------|-------------------------------------------------------------------------|\n| **Framework**    | Keras (TF backend) for Modeling \u0026 Evaluation; TensorFlow Data for Pipeline|\n| **Optimizer**    | Nadam (Nesterov‑accelerated Adam) with Gradient Clipping                |\n| **Learning Rate**| 1e‑3 with Exponential Decay: $lr \\times 0.1^{\\tfrac{\\text{epoch}}{s}}$  |\n| **Regularizers** | L2, Weight Decay, , Dropout, Early Stopping                             |\n| **Loss**         | Cross Entropy                                                           |\n| **Metrics**      | Accuracy, Perplexity (Exponentiated Cross‑Entropy)                      |\n| **Num of Epochs**| 5, Triggered by Early Stopping                                          |\n| **Batch Size**   | 256                                                                     |\n| **Callbacks**    | ModelCheckpoint, EarlyStopping, LearningRateScheduler                   |\n\n## 🎶 Inference\n\nTo generate new music, the process begins by **sampling the next note** from the model’s output distribution. This is done with `predict_next_token`, which takes the current context, scales the logits by a chosen **temperature** (to control creativity), and samples the next token.  \n\nBuilding on this, `generate_sequence` repeatedly applies this step in an **autoregressive loop**, appending each newly predicted token back into the context. In this way, a short seed sequence can be extended into a much longer continuation, capturing Bach‑style harmonic and melodic flow.  \n\nFinally, `generate_chorale` wraps the entire process: it selects a seed sequence (optionally using `draw_random_sample`), generates a continuation, converts the resulting tokens back into four‑voice chords, and saves the output as a **MIDI file**.\n\nExample usage:\n\n```python\nfrom src.inference import generate_chorale, draw_random_sample\n# pick a random seed from dataset\nseed_path = draw_random_sample(\"data/jsb_chorales_extracted/jsb_chorales/val\")\n\n# generate and save a chorale\ngenerate_chorale(\n    model, \n    sample_seed_path=seed_path, \n    note2id=note2id, \n    id2note=id2note, \n    file_name=\"samples/chorale.mid\",\n    max_len=25, \n    temperature=0.9,\n    include_init_context=False\n)\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoom4n%2Fbachnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhoom4n%2Fbachnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoom4n%2Fbachnet/lists"}