{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tensor Train layers for neural networks\n", "\n", "In this section, the TT layers are introduced.\n", "\n", "Imports:" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "import torch as tn\n", "import torch.nn as nn\n", "import datetime\n", "try:\n", " import torchtt as tntt\n", "except:\n", " print('Installing torchTT...')\n", " %pip install git+https://github.com/ion-g-ion/torchTT\n", " import torchtt as tntt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We consider a linear layer $\\mathcal{LTT}(\\mathsf{x}) = \\mathsf{Wx}+\\mathsf{b}$ acting on a tensor input $\\mathsf{x}$ of shape $n_1 \\times \\cdots \\times n_d$ and returning a tensor of shape $m_1\\times\\cdots\\times m_d$. The corresponding weight matrix $\\mathsf{W}$ would have the shape $(m_1\\times\\cdots\\times m_d) \\times (n_1 \\times \\cdots \\times n_d)$. The goal is to represent the weights tensor operator in TT format and perform the learning with respect tot the cores of the TT decomposition (ranks have to be fixed a priori).\n", "Due to the AD functionality of `torchtt`, the gradient with respect tot the cores can be computed for any network structure.\n", "TT layers can be added using `torchtt.nn.LinearLayerTT()` class. \n", "\n", "In the following, a neural netywork with 3 hidden layers and one linear layer is created.\n", "The shapes of the individual layers are \n", "\n", "$\\mathbb{R}^{16} \\times\\mathbb{R}^{16} \\times\\mathbb{R}^{16} \\times\\mathbb{R}^{16} \\underset{}{\\longrightarrow} \\mathbb{R}^8 \\times\\mathbb{R}^8 \\times\\mathbb{R}^8 \\times\\mathbb{R}^8 \\underset{}{\\longrightarrow} \\mathbb{R}^4 \\times\\mathbb{R}^4 \\times\\mathbb{R}^4 \\times\\mathbb{R}^4 \\underset{}{\\longrightarrow} \\mathbb{R}^2 \\times\\mathbb{R}^4 \\times\\mathbb{R}^2 \\times\\mathbb{R}^4 \\underset{}{\\longrightarrow} \\mathbb{R}^{10}$.\n", "\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "class BasicTT(nn.Module):\n", " def __init__(self):\n", " super().__init__()\n", " self.ttl1 = tntt.nn.LinearLayerTT([16,16,16,16], [8,8,8,8], [1,3,3,3,1])\n", " self.ttl2 = tntt.nn.LinearLayerTT([8,8,8,8], [4,4,4,4], [1,2,2,2,1])\n", " self.ttl3 = tntt.nn.LinearLayerTT([4,4,4,4], [2,4,2,4], [1,2,2,2,1])\n", " self.linear = nn.Linear(64, 10, dtype = tn.float32)\n", "\n", " def forward(self, x):\n", " x = self.ttl1(x)\n", " x = tn.relu(x)\n", " x = self.ttl2(x)\n", " x = tn.relu(x)\n", " x = self.ttl3(x)\n", " x = tn.relu(x)\n", " x = tn.reshape(x,[-1,64])\n", " return self.linear(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Create the model and print the number of trainable parameters as well as the model structure." ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Number of trainable parameters: 17\n", "BasicTT(\n", " (ttl1): LinearLayerTT(\n", " (cores): ParameterList(\n", " (0): Parameter containing: [torch.float32 of size 1x8x16x3]\n", " (1): Parameter containing: [torch.float32 of size 3x8x16x3]\n", " (2): Parameter containing: [torch.float32 of size 3x8x16x3]\n", " (3): Parameter containing: [torch.float32 of size 3x8x16x1]\n", " )\n", " )\n", " (ttl2): LinearLayerTT(\n", " (cores): ParameterList(\n", " (0): Parameter containing: [torch.float32 of size 1x4x8x2]\n", " (1): Parameter containing: [torch.float32 of size 2x4x8x2]\n", " (2): Parameter containing: [torch.float32 of size 2x4x8x2]\n", " (3): Parameter containing: [torch.float32 of size 2x4x8x1]\n", " )\n", " )\n", " (ttl3): LinearLayerTT(\n", " (cores): ParameterList(\n", " (0): Parameter containing: [torch.float32 of size 1x2x4x2]\n", " (1): Parameter containing: [torch.float32 of size 2x4x4x2]\n", " (2): Parameter containing: [torch.float32 of size 2x2x4x2]\n", " (3): Parameter containing: [torch.float32 of size 2x4x4x1]\n", " )\n", " )\n", " (linear): Linear(in_features=64, out_features=10, bias=True)\n", ")\n" ] } ], "source": [ "model = BasicTT()\n", "print('Number of trainable parameters:', len(list(model.parameters())))\n", "print(model)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A random input is created and passed as argument to the model. Batch evaluation is also possible by extending the dimensionality of the input before the leading mode." ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 3.61 s, sys: 2.22 s, total: 5.84 s\n", "Wall time: 450 ms\n" ] } ], "source": [ "input = tn.rand((16,16,16,16), dtype = tn.float32)\n", "pred = model.forward(input)\n", "\n", "input_batch = tn.rand((1000,16,16,16,16), dtype = tn.float32)\n", "label_batch = tn.rand((1000,10), dtype = tn.float32)\n", "%time pred = model.forward(input_batch)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The obtained network can be trained similarily to other `torch` models.\n", "A loss function together with an optimizer are defined. " ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "criterion = nn.CrossEntropyLoss()\n", "optimizer = tn.optim.Adam(model.parameters(), lr = 0.001) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A training loop is executed to exemplify the training parameters update procedure. An example where a true dataset is used is presented [here](https://github.com/ion-g-ion/torchTT/blob/main/examples/mnist_nn.ipynb). " ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1, loss 1.196611e+01\n", "Epoch 2, loss 1.182479e+01\n", "Epoch 3, loss 1.174133e+01\n", "Epoch 4, loss 1.169423e+01\n", "Epoch 5, loss 1.166731e+01\n", "Finished Training\n", "CPU times: user 3.85 s, sys: 1.79 s, total: 5.64 s\n", "Wall time: 499 ms\n" ] } ], "source": [ "for epoch in range(5): \n", "\n", " optimizer.zero_grad()\n", "\n", " outputs = model(input_batch)\n", " loss = criterion(outputs, label_batch)\n", " loss.backward()\n", " optimizer.step()\n", "\n", " # print statistics\n", " print('Epoch %d, loss %e'%(epoch+1,loss.item()))\n", "\n", "\n", "print('Finished Training')\n", "%time plm = model(input_batch)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the GPU is available, the model can be run on it to get a speedup (should be run 2 times to see the speedup due to CUDA warm-up)." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Time on CPU 0:00:00.232971\n", "Time on GPU 0:00:00.003855\n" ] } ], "source": [ "if tn.cuda.is_available():\n", " model_gpu = BasicTT().cuda()\n", " input_batch_gpu = tn.rand((400,16,16,16,16)).cuda()\n", "\n", " input_batch = tn.rand((400,16,16,16,16))\n", " tme = datetime.datetime.now()\n", " pred = model.forward(input_batch)\n", " tme = datetime.datetime.now() - tme\n", " print('Time on CPU ',tme)\n", "\n", " tme = datetime.datetime.now()\n", " pred_gpu = model_gpu.forward(input_batch_gpu).cpu()\n", " tme = datetime.datetime.now() - tme\n", " print('Time on GPU ',tme)\n", "\n", " " ] } ], "metadata": { "kernelspec": { "display_name": "pytorch", "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.15" }, "orig_nbformat": 4 }, "nbformat": 4, "nbformat_minor": 2 }