{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":3004,"databundleVersionId":861823,"sourceType":"competition"}],"dockerImageVersionId":31192,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:05:59.784613Z","iopub.execute_input":"2025-12-18T21:05:59.785155Z","iopub.status.idle":"2025-12-18T21:05:59.798171Z","shell.execute_reply.started":"2025-12-18T21:05:59.785123Z","shell.execute_reply":"2025-12-18T21:05:59.797606Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Impelementation for CNN from scratch using numpy only","metadata":{}},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:05:59.799414Z","iopub.execute_input":"2025-12-18T21:05:59.799749Z","iopub.status.idle":"2025-12-18T21:05:59.811338Z","shell.execute_reply.started":"2025-12-18T21:05:59.799732Z","shell.execute_reply":"2025-12-18T21:05:59.810658Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df = pd.read_csv(\"/kaggle/input/digit-recognizer/train.csv\")\n\nnp.random.seed(42)\nperm = np.random.permutation(len(df))\ndf = df.iloc[perm].reset_index(drop=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:05:59.812005Z","iopub.execute_input":"2025-12-18T21:05:59.812307Z","iopub.status.idle":"2025-12-18T21:06:01.768364Z","shell.execute_reply.started":"2025-12-18T21:05:59.812291Z","shell.execute_reply":"2025-12-18T21:06:01.767486Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X = df.iloc[:, 1:].values\ny = df.iloc[:, 0].values\n\nX = X.reshape(len(X), 28, 28)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:06:01.770004Z","iopub.execute_input":"2025-12-18T21:06:01.770321Z","iopub.status.idle":"2025-12-18T21:06:01.774545Z","shell.execute_reply.started":"2025-12-18T21:06:01.770301Z","shell.execute_reply":"2025-12-18T21:06:01.773867Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"split_ratio = 0.67\nsplit_idx = int(len(X) * split_ratio)\n\nX_train = X[:split_idx]\ny_train = y[:split_idx]\n\nX_test  = X[split_idx:]\ny_test  = y[split_idx:]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:06:01.775264Z","iopub.execute_input":"2025-12-18T21:06:01.775543Z","iopub.status.idle":"2025-12-18T21:06:01.794951Z","shell.execute_reply.started":"2025-12-18T21:06:01.775528Z","shell.execute_reply":"2025-12-18T21:06:01.794452Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"X_train:\", X_train.shape)\nprint(\"y_train:\", y_train.shape)\nprint(\"X_test :\", X_test.shape)\nprint(\"y_test :\", y_test.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:06:01.795536Z","iopub.execute_input":"2025-12-18T21:06:01.795768Z","iopub.status.idle":"2025-12-18T21:06:01.816453Z","shell.execute_reply.started":"2025-12-18T21:06:01.795745Z","shell.execute_reply":"2025-12-18T21:06:01.815699Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class Conv3x3:\n    def __init__(self, num_filters):\n        self.num_filters = num_filters\n        self.filters = np.random.randn(num_filters, 3, 3) / 9\n\n    def iterate_regions(self, image):\n        h, w = image.shape\n        for i in range(h - 2):\n            for j in range(w - 2):\n                yield image[i:i+3, j:j+3], i, j\n\n    def forward(self, input):\n        self.last_input = input\n        h, w = input.shape\n        output = np.zeros((h - 2, w - 2, self.num_filters))\n\n        for region, i, j in self.iterate_regions(input):\n            output[i, j] = np.sum(region * self.filters, axis=(1,2))\n        return output\n\n    def backprop(self, d_out, lr):\n        d_filters = np.zeros(self.filters.shape)\n\n        for region, i, j in self.iterate_regions(self.last_input):\n            for f in range(self.num_filters):\n                d_filters[f] += d_out[i, j, f] * region\n\n        self.filters -= lr * d_filters\n        return None","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:06:01.817368Z","iopub.execute_input":"2025-12-18T21:06:01.817614Z","iopub.status.idle":"2025-12-18T21:06:01.833459Z","shell.execute_reply.started":"2025-12-18T21:06:01.817593Z","shell.execute_reply":"2025-12-18T21:06:01.832925Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class MaxPool2:\n    def iterate_regions(self, image):\n        h, w, _ = image.shape\n        for i in range(h // 2):\n            for j in range(w // 2):\n                yield image[i*2:i*2+2, j*2:j*2+2], i, j\n\n    def forward(self, input):\n        self.last_input = input\n        h, w, f = input.shape\n        output = np.zeros((h//2, w//2, f))\n\n        for region, i, j in self.iterate_regions(input):\n            output[i, j] = np.max(region, axis=(0,1))\n        return output\n\n    def backprop(self, d_out):\n        d_input = np.zeros(self.last_input.shape)\n\n        for region, i, j in self.iterate_regions(self.last_input):\n            h, w, f = region.shape\n            max_val = np.max(region, axis=(0,1))\n\n            for i2 in range(h):\n                for j2 in range(w):\n                    for f2 in range(f):\n                        if region[i2, j2, f2] == max_val[f2]:\n                            d_input[i*2+i2, j*2+j2, f2] = d_out[i, j, f2]\n        return d_input\n\nclass Softmax:\n    def __init__(self, input_len, nodes):\n        self.weights = np.random.randn(input_len, nodes) / input_len\n        self.biases = np.zeros(nodes)\n\n    def forward(self, input):\n        self.last_input_shape = input.shape\n        input = input.flatten()\n        self.last_input = input\n\n        totals = np.dot(input, self.weights) + self.biases\n        self.last_totals = totals\n\n        exp = np.exp(totals - np.max(totals))\n        return exp / np.sum(exp)\n\n    def backprop(self, d_out, lr):\n        d_w = np.outer(self.last_input, d_out)\n        d_b = d_out\n        d_inputs = np.dot(self.weights, d_out)\n\n        self.weights -= lr * d_w\n        self.biases  -= lr * d_b\n\n        return d_inputs.reshape(self.last_input_shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:06:01.834771Z","iopub.execute_input":"2025-12-18T21:06:01.834987Z","iopub.status.idle":"2025-12-18T21:06:01.855140Z","shell.execute_reply.started":"2025-12-18T21:06:01.834972Z","shell.execute_reply":"2025-12-18T21:06:01.854452Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def relu(x):\n    return np.maximum(0, x)\n\ndef relu_backprop(d_out, x):\n    return d_out * (x > 0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:06:01.855982Z","iopub.execute_input":"2025-12-18T21:06:01.856236Z","iopub.status.idle":"2025-12-18T21:06:01.884847Z","shell.execute_reply.started":"2025-12-18T21:06:01.856214Z","shell.execute_reply":"2025-12-18T21:06:01.884141Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"conv = Conv3x3(8)\npool = MaxPool2()\nsoftmax = Softmax(13 * 13 * 8, 10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:06:01.886057Z","iopub.execute_input":"2025-12-18T21:06:01.886293Z","iopub.status.idle":"2025-12-18T21:06:01.902694Z","shell.execute_reply.started":"2025-12-18T21:06:01.886271Z","shell.execute_reply":"2025-12-18T21:06:01.902032Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def forward(image, label):\n    out = conv.forward((image / 255) - 0.5)\n    out_relu = relu(out)\n    out_pool = pool.forward(out_relu)\n    out_soft = softmax.forward(out_pool)\n\n    loss = -np.log(out_soft[label])\n    acc = 1 if np.argmax(out_soft) == label else 0\n\n    cache = (out, out_relu, out_pool, out_soft)\n    return out_soft, loss, acc, cache\n\n\ndef train(image, label, lr=0.005):\n    out, loss, acc, cache = forward(image, label)\n    out_conv, out_relu, out_pool, out_soft = cache\n\n    gradient = out_soft.copy()\n    gradient[label] -= 1\n\n    grad = softmax.backprop(gradient, lr)\n    grad = pool.backprop(grad)\n    grad = relu_backprop(grad, out_conv)\n    conv.backprop(grad, lr)\n\n    return loss, acc","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:06:01.903469Z","iopub.execute_input":"2025-12-18T21:06:01.903680Z","iopub.status.idle":"2025-12-18T21:06:01.922286Z","shell.execute_reply.started":"2025-12-18T21:06:01.903666Z","shell.execute_reply":"2025-12-18T21:06:01.921623Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for epoch in range(1):\n    print(f\"Epoch {epoch+1}\")\n\n    perm = np.random.permutation(len(X_train))\n    X_train = X_train[perm]\n    y_train = y_train[perm]\n\n    loss = 0\n    num_correct = 0\n\n    for i in range(len(X_train)):\n        l, acc = train(X_train[i], y_train[i])\n        loss += l\n        num_correct += acc\n\n        if i > 0 and i % 2000 == 1999:\n            print(\n                \"[Step {}] Avg Loss {:.3f} | Accuracy {:.2f}%\"\n                .format(i+1, loss/2000, num_correct/20)\n            )\n            loss = 0\n            num_correct = 0","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-12-18T21:06:01.922856Z","iopub.execute_input":"2025-12-18T21:06:01.923062Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"loss = 0\nnum_correct = 0\n\nfor i in range(len(X_test)):\n    out, l, acc, _ = forward(X_test[i], y_test[i])\n    loss += l\n    num_correct += acc\n\nprint(\"Test Loss:\", loss / len(X_test))\nprint(\"Test Accuracy:\", num_correct / len(X_test))","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}