mirror of
https://github.com/ml-explore/mlx-examples.git
synced 2025-06-25 01:41:19 +08:00
523 lines
59 KiB
Plaintext
523 lines
59 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Import Library"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import mnist"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 2,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"import mlx.core as mx\n",
|
|
"import mlx.nn as nn\n",
|
|
"import mlx.optimizers as optim\n",
|
|
"\n",
|
|
"from tqdm import tqdm\n",
|
|
"import numpy as np\n",
|
|
"import matplotlib.pyplot as plt"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# GAN Architecture"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Generator 👨🏻🎨"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 3,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def GenBlock(in_dim:int,out_dim:int):\n",
|
|
" \n",
|
|
" return nn.Sequential(\n",
|
|
" nn.Linear(in_dim,out_dim),\n",
|
|
" nn.BatchNorm(out_dim),\n",
|
|
" nn.ReLU()\n",
|
|
" )"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 4,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Generator(nn.Module):\n",
|
|
"\n",
|
|
" def __init__(self, z_dim:int = 10, im_dim:int = 784, hidden_dim: int =128):\n",
|
|
" super(Generator, self).__init__()\n",
|
|
" # Build the neural network\n",
|
|
" self.gen = nn.Sequential(\n",
|
|
" GenBlock(z_dim, hidden_dim),\n",
|
|
" GenBlock(hidden_dim, hidden_dim * 2),\n",
|
|
" GenBlock(hidden_dim * 2, hidden_dim * 4),\n",
|
|
" GenBlock(hidden_dim * 4, hidden_dim * 8),\n",
|
|
"\n",
|
|
"\n",
|
|
" nn.Linear(hidden_dim * 8,im_dim),\n",
|
|
" nn.Sigmoid()\n",
|
|
" )\n",
|
|
" \n",
|
|
" def __call__(self, noise):\n",
|
|
"\n",
|
|
" return self.gen(noise)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"Generator(\n",
|
|
" (gen): Sequential(\n",
|
|
" (layers.0): Sequential(\n",
|
|
" (layers.0): Linear(input_dims=100, output_dims=128, bias=True)\n",
|
|
" (layers.1): BatchNorm(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
|
|
" (layers.2): ReLU()\n",
|
|
" )\n",
|
|
" (layers.1): Sequential(\n",
|
|
" (layers.0): Linear(input_dims=128, output_dims=256, bias=True)\n",
|
|
" (layers.1): BatchNorm(256, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
|
|
" (layers.2): ReLU()\n",
|
|
" )\n",
|
|
" (layers.2): Sequential(\n",
|
|
" (layers.0): Linear(input_dims=256, output_dims=512, bias=True)\n",
|
|
" (layers.1): BatchNorm(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
|
|
" (layers.2): ReLU()\n",
|
|
" )\n",
|
|
" (layers.3): Sequential(\n",
|
|
" (layers.0): Linear(input_dims=512, output_dims=1024, bias=True)\n",
|
|
" (layers.1): BatchNorm(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)\n",
|
|
" (layers.2): ReLU()\n",
|
|
" )\n",
|
|
" (layers.4): Linear(input_dims=1024, output_dims=784, bias=True)\n",
|
|
" (layers.5): Sigmoid()\n",
|
|
" )\n",
|
|
")"
|
|
]
|
|
},
|
|
"execution_count": 5,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"gen = Generator(100)\n",
|
|
"gen"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def get_noise(n_samples, z_dim):\n",
|
|
" return np.random.randn(n_samples,z_dim)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Discriminator 🕵🏻♂️"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 7,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def DisBlock(in_dim:int,out_dim:int):\n",
|
|
" return nn.Sequential(\n",
|
|
" nn.Linear(in_dim,out_dim),\n",
|
|
" nn.LeakyReLU(negative_slope=0.2)\n",
|
|
" )"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 8,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"class Discriminator(nn.Module):\n",
|
|
"\n",
|
|
" def __init__(self,im_dim:int = 784, hidden_dim:int = 128):\n",
|
|
" super(Discriminator, self).__init__()\n",
|
|
"\n",
|
|
" self.disc = nn.Sequential(\n",
|
|
" DisBlock(im_dim, hidden_dim * 4),\n",
|
|
" DisBlock(hidden_dim * 4, hidden_dim * 2),\n",
|
|
" DisBlock(hidden_dim * 2, hidden_dim),\n",
|
|
"\n",
|
|
" nn.Linear(hidden_dim,1),\n",
|
|
" )\n",
|
|
" \n",
|
|
" def __call__(self, noise):\n",
|
|
"\n",
|
|
" return self.disc(noise)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"Discriminator(\n",
|
|
" (disc): Sequential(\n",
|
|
" (layers.0): Sequential(\n",
|
|
" (layers.0): Linear(input_dims=784, output_dims=512, bias=True)\n",
|
|
" (layers.1): LeakyReLU()\n",
|
|
" )\n",
|
|
" (layers.1): Sequential(\n",
|
|
" (layers.0): Linear(input_dims=512, output_dims=256, bias=True)\n",
|
|
" (layers.1): LeakyReLU()\n",
|
|
" )\n",
|
|
" (layers.2): Sequential(\n",
|
|
" (layers.0): Linear(input_dims=256, output_dims=128, bias=True)\n",
|
|
" (layers.1): LeakyReLU()\n",
|
|
" )\n",
|
|
" (layers.3): Linear(input_dims=128, output_dims=1, bias=True)\n",
|
|
" )\n",
|
|
")"
|
|
]
|
|
},
|
|
"execution_count": 9,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"disc = Discriminator()\n",
|
|
"disc"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"# Model Training 🏋🏻♂️"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 10,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"# Set your parameters\n",
|
|
"criterion = nn.losses.binary_cross_entropy\n",
|
|
"n_epochs = 200\n",
|
|
"z_dim = 64\n",
|
|
"display_step = 500\n",
|
|
"batch_size = 128\n",
|
|
"lr = 0.00001"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 11,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"gen = Generator(z_dim)\n",
|
|
"mx.eval(gen.parameters())\n",
|
|
"gen_opt = optim.Adam(learning_rate=lr)\n",
|
|
"\n",
|
|
"disc = Discriminator()\n",
|
|
"mx.eval(disc.parameters())\n",
|
|
"disc_opt = optim.Adam(learning_rate=lr)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"## Losses"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def disc_loss(gen, disc, real, num_images, z_dim):\n",
|
|
" noise = mx.array(get_noise(num_images, z_dim))\n",
|
|
" fake_images = gen(noise)\n",
|
|
" \n",
|
|
" fake_disc = disc(fake_images)\n",
|
|
" \n",
|
|
" fake_labels = mx.zeros((fake_images.shape[0],1))\n",
|
|
" fake_loss = nn.losses.binary_cross_entropy(fake_disc,fake_labels,with_logits=True)\n",
|
|
" \n",
|
|
" real_disc = disc(real)\n",
|
|
" real_labels = mx.ones((real.shape[0],1))\n",
|
|
"\n",
|
|
" real_loss = nn.losses.binary_cross_entropy(real_disc,real_labels,with_logits=True)\n",
|
|
"\n",
|
|
" disc_loss = (fake_loss + real_loss) / 2\n",
|
|
"\n",
|
|
" return disc_loss"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 13,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def gen_loss(gen, disc, num_images, z_dim):\n",
|
|
"\n",
|
|
" noise = mx.array(get_noise(num_images, z_dim))\n",
|
|
" fake_images = gen(noise)\n",
|
|
" fake_disc = disc(fake_images)\n",
|
|
"\n",
|
|
" fake_labels = mx.ones((fake_images.shape[0],1))\n",
|
|
" \n",
|
|
" gen_loss = nn.losses.binary_cross_entropy(fake_disc,fake_labels,with_logits=True)\n",
|
|
"\n",
|
|
" return gen_loss"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 14,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"train_images, _, test_images, _ = map(\n",
|
|
" mx.array, getattr(mnist, 'mnist')()\n",
|
|
")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 15,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def batch_iterate(batch_size:int, ipt:list):\n",
|
|
" perm = mx.array(np.random.permutation(len(ipt)))\n",
|
|
" for s in range(0, ipt.size, batch_size):\n",
|
|
" ids = perm[s : s + batch_size]\n",
|
|
" yield ipt[ids]"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "markdown",
|
|
"metadata": {},
|
|
"source": [
|
|
"### show batch of images"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"image/png": "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",
|
|
"text/plain": [
|
|
"<Figure size 400x400 with 16 Axes>"
|
|
]
|
|
},
|
|
"metadata": {},
|
|
"output_type": "display_data"
|
|
}
|
|
],
|
|
"source": [
|
|
"for X in batch_iterate(16, train_images):\n",
|
|
" fig,axes = plt.subplots(4, 4, figsize=(4, 4))\n",
|
|
"\n",
|
|
" for i, ax in enumerate(axes.flat):\n",
|
|
" img = mx.array(X[i]).reshape(28,28)\n",
|
|
" ax.imshow(img,cmap='gray')\n",
|
|
" ax.axis('off')\n",
|
|
" break"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 17,
|
|
"metadata": {},
|
|
"outputs": [],
|
|
"source": [
|
|
"def show_images(imgs:list[int],num_imgs:int = 25):\n",
|
|
" fig,axes = plt.subplots(5, 5, figsize=(4, 4))\n",
|
|
" \n",
|
|
" for i, ax in enumerate(axes.flat):\n",
|
|
" img = mx.array(imgs[i]).reshape(28,28)\n",
|
|
" ax.imshow(img,cmap='gray')\n",
|
|
" ax.axis('off')\n",
|
|
" plt.show()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 18,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"array(0.738084, dtype=float32)"
|
|
]
|
|
},
|
|
"execution_count": 18,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"z_dim = 64\n",
|
|
"gen = Generator(z_dim)\n",
|
|
"mx.eval(gen.parameters())\n",
|
|
"gen_opt = optim.Adam(learning_rate=lr)\n",
|
|
"\n",
|
|
"disc = Discriminator()\n",
|
|
"mx.eval(disc.parameters())\n",
|
|
"disc_opt = optim.Adam(learning_rate=lr)\n",
|
|
"\n",
|
|
"g_loss = gen_loss(gen, disc, 8, z_dim)\n",
|
|
"g_loss\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 19,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"data": {
|
|
"text/plain": [
|
|
"60000"
|
|
]
|
|
},
|
|
"execution_count": 19,
|
|
"metadata": {},
|
|
"output_type": "execute_result"
|
|
}
|
|
],
|
|
"source": [
|
|
"len(train_images)"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 22,
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stderr",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" 0%| | 0/200 [00:00<?, ?it/s]"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"batch_size = 16\n",
|
|
"display_step = 50\n",
|
|
"cur_step = 0\n",
|
|
"mean_generator_loss = 0\n",
|
|
"mean_discriminator_loss = 0\n",
|
|
"\n",
|
|
"D_loss_grad = nn.value_and_grad(disc, disc_loss)\n",
|
|
"G_loss_grad = nn.value_and_grad(gen, gen_loss)\n",
|
|
"\n",
|
|
"\n",
|
|
"for epoch in tqdm(range(200)):\n",
|
|
"\n",
|
|
" for real in batch_iterate(batch_size, train_images):\n",
|
|
" cur_batch_size = len(real)\n",
|
|
" # real = real.reshape(-1)\n",
|
|
" \n",
|
|
" # Flatten the batch of real images from the dataset\n",
|
|
" \n",
|
|
" # plt.imshow(real[0].reshape(28,28))\n",
|
|
" # print(len(real))\n",
|
|
" # break\n",
|
|
" \n",
|
|
" D_loss,D_grads = D_loss_grad(gen, disc, real, batch_size, z_dim)\n",
|
|
"\n",
|
|
" # Update optimizer\n",
|
|
" disc_opt.update(disc, D_grads)\n",
|
|
" \n",
|
|
" # Update gradients\n",
|
|
" mx.eval(disc.parameters(), disc_opt.state)\n",
|
|
"\n",
|
|
" G_loss,G_grads = G_loss_grad(gen, disc, batch_size, z_dim)\n",
|
|
" \n",
|
|
" # Update optimizer\n",
|
|
" gen_opt.update(gen, G_grads)\n",
|
|
" \n",
|
|
" # Update gradients\n",
|
|
" mx.eval(gen.parameters(), gen_opt.state)\n",
|
|
" \n",
|
|
" if cur_step % display_step == 0 and cur_step > 0:\n",
|
|
" print(f\"Step {epoch}: Generator loss: {G_loss}, discriminator loss: {D_loss}\")\n",
|
|
" fake_noise = mx.array(get_noise(batch_size, z_dim))\n",
|
|
" fake = gen(fake_noise)\n",
|
|
" show_images(fake)\n",
|
|
" show_images(real)\n",
|
|
" cur_step += 1"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "base",
|
|
"language": "python",
|
|
"name": "python3"
|
|
},
|
|
"language_info": {
|
|
"codemirror_mode": {
|
|
"name": "ipython",
|
|
"version": 3
|
|
},
|
|
"file_extension": ".py",
|
|
"mimetype": "text/x-python",
|
|
"name": "python",
|
|
"nbconvert_exporter": "python",
|
|
"pygments_lexer": "ipython3",
|
|
"version": "3.10.10"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 2
|
|
}
|