{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Digit recognizer project","metadata":{}},{"cell_type":"markdown","source":"According to the documentation of this competition, we have: the data files train.csv and test.csv which contain gray-scale images of hand-drawn digits, from zero through nine.\n\nEach image is 28 pixels in height and 28 pixels in width, for a total of 784 pixels in total. Each pixel has a single pixel-value associated with it, indicating the lightness or darkness of that pixel, with higher numbers meaning darker. This pixel-value is an integer between 0 and 255, inclusive.\n\nWe will build an approach to solve this problem based dimentionality reduction using PCA (Principal Component Analysis) and SVM (Support Vector Machine) classifier.","metadata":{}},{"cell_type":"code","source":"# Imports\nimport os\nimport random\nimport warnings\n\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport pandas as pd\nimport seaborn as sb\n\nfrom sklearn.decomposition import PCA\nfrom sklearn.manifold import TSNE\nfrom sklearn.metrics import accuracy_score\nfrom sklearn.metrics import confusion_matrix\nfrom sklearn.model_selection import cross_validate \nfrom sklearn.model_selection import StratifiedKFold \nfrom sklearn.model_selection import train_test_split\nfrom sklearn.svm import SVC","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2023-02-23T15:05:59.143219Z","iopub.execute_input":"2023-02-23T15:05:59.143757Z","iopub.status.idle":"2023-02-23T15:05:59.151350Z","shell.execute_reply.started":"2023-02-23T15:05:59.143716Z","shell.execute_reply":"2023-02-23T15:05:59.150229Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Filter warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Functions\ndef show_image(data: np.array, target: np.array) -> None:\n    \"\"\"\n    This function take the a random image from the \n    the array that contains images, reshape it and\n    plot it, with as title the truth value of the image.\n    \"\"\"\n    \n    # Reshape the data, to make 28x28 images\n    images = data.reshape(-1, 28, 28) \n    # -1 tells numpy that the value must be inferred \n    # from the length of the array and remaining dimensions.\n    \n    idx = random.randint(0, len(images))\n                   \n    # Plot a random image\n    plt.figure(figsize=(3,3))\n    plt.imshow(images[idx])\n    plt.title(f\"Real number: {target[idx]}\")\n    plt.show()\n    \ndef compare_images(\n    data: np.array,\n    data_recovered: np.array,\n    target: np.array\n) -> None:\n    \"\"\"\n    This function take the a random image from the \n    the array that contains recovered after PCA, \n    and the array that contains images, reshape it\n    and plot both with as title the truth value of \n    the images.\n    \"\"\"\n    \n    idx = random.randint(0, len(data))\n    \n    # Plot\n    plt.subplot(1, 2, 1)\n    plt.imshow(data[idx, :].reshape((28,28)))\n    plt.title(f\"Original \\n Real number: {target[idx]}\")\n    plt.subplot(1, 2, 2)\n    plt.imshow(data_recovered[idx, :].reshape((28,28)))\n    plt.title(f\"Compressed \\n Real number: {target[idx]}\")\n    plt.show()\n    \ndef predict_image(data: np.array, target: np.array) -> None:\n    \"\"\"\n    This function take the a random image from the \n    the array that contains test images, reshape it \n    and plot it, with as title the predicted value \n    of the image.\n    \"\"\"\n    \n    # Reshape the data, to make 28x28 images\n    images = data.reshape(-1, 28, 28) \n    # -1 tells numpy that the value must be inferred \n    # from the length of the array and remaining dimensions.\n    \n    idx = random.randint(0, len(images))\n                   \n    # Plot a random image\n    plt.figure(figsize=(3,3))\n    plt.imshow(images[idx])\n    plt.title(f\"Predicted number: {target[idx]}\")\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:58:03.345483Z","iopub.execute_input":"2023-02-23T14:58:03.345980Z","iopub.status.idle":"2023-02-23T14:58:03.358426Z","shell.execute_reply.started":"2023-02-23T14:58:03.345925Z","shell.execute_reply":"2023-02-23T14:58:03.357379Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Exploratory Data Analysis","metadata":{}},{"cell_type":"code","source":"# Check files avaliable\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    for filename in filenames:\n        print(os.path.join(dirname, filename))","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:58:06.518508Z","iopub.execute_input":"2023-02-23T14:58:06.519240Z","iopub.status.idle":"2023-02-23T14:58:06.528501Z","shell.execute_reply.started":"2023-02-23T14:58:06.519198Z","shell.execute_reply":"2023-02-23T14:58:06.527179Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Read train, test and submission csv\ndf_train = pd.read_csv(\"/kaggle/input/digit-recognizer/train.csv\")\ndf_test = pd.read_csv(\"/kaggle/input/digit-recognizer/test.csv\")\ndf_submission = pd.read_csv(\"/kaggle/input/digit-recognizer/sample_submission.csv\")\n\n# Display dimensions of each dataframe\nprint(f\"The dimension of the train df is: {df_train.shape}\")\nprint(f\"The dimension of the test df is: {df_test.shape}\")\nprint(f\"The dimension of the submission df is: {df_submission.shape}\")","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:58:08.446833Z","iopub.execute_input":"2023-02-23T14:58:08.447348Z","iopub.status.idle":"2023-02-23T14:58:12.669527Z","shell.execute_reply.started":"2023-02-23T14:58:08.447305Z","shell.execute_reply":"2023-02-23T14:58:12.668275Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here we have the 28k images in the test sample that need to be predict. The result will be store in the submission dataframe, and return as csv to kaggle for evaluation.","metadata":{}},{"cell_type":"code","source":"# Analyze the train dataframe\ndf_train.head()","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:58:12.671944Z","iopub.execute_input":"2023-02-23T14:58:12.672367Z","iopub.status.idle":"2023-02-23T14:58:12.693028Z","shell.execute_reply.started":"2023-02-23T14:58:12.672330Z","shell.execute_reply":"2023-02-23T14:58:12.691451Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The label, your target, is located in the first column of the dataframe.  \nSo we can already seperate the X_train and y_train using our df_train data.","metadata":{}},{"cell_type":"code","source":"# Extract the target from the data of the train dataframe\n# And convert into numpy array\nX_train = df_train.drop(labels='label', axis=1).values\ny_train = df_train['label'].values\nX_test = df_test.values","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:58:18.193884Z","iopub.execute_input":"2023-02-23T14:58:18.194399Z","iopub.status.idle":"2023-02-23T14:58:18.366061Z","shell.execute_reply.started":"2023-02-23T14:58:18.194357Z","shell.execute_reply":"2023-02-23T14:58:18.364841Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Repartition of the classes across the dataset\nplt.figure(figsize=(12,5))\nsb.countplot(data=df_train, x='label')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:54:10.908945Z","iopub.execute_input":"2023-02-23T14:54:10.909457Z","iopub.status.idle":"2023-02-23T14:54:11.191132Z","shell.execute_reply.started":"2023-02-23T14:54:10.909415Z","shell.execute_reply":"2023-02-23T14:54:11.189996Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We have a fairly balanced dataset here, no need to apply some methods to deal with imbalanced dataset. ","metadata":{}},{"cell_type":"code","source":"# Display an image as example\nshow_image(X_train, y_train)","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:54:18.519535Z","iopub.execute_input":"2023-02-23T14:54:18.520006Z","iopub.status.idle":"2023-02-23T14:54:18.706212Z","shell.execute_reply.started":"2023-02-23T14:54:18.519955Z","shell.execute_reply":"2023-02-23T14:54:18.705054Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"TSNE for T-distributed Stochastic Neighboor Embedding is a dimensionality reduction technique. It reduces dimensionality while trying to keep similar instances close and dissimilar instances apart.  \nIt is mostly used for visualization, in particular to visualize clusters of instances in high-dimensional space, like our dataset.\n\nBut before using dimensionality reduction techniques, we will normalize our data, that help to run faster the differents algorithm, and ensure more precision. Since each pixel is between 0 to 255, we can just divide all the array by 255.","metadata":{}},{"cell_type":"code","source":"# Normalize the data\nX_train = X_train / 255.0\nX_test = X_test / 255.0","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:58:23.262345Z","iopub.execute_input":"2023-02-23T14:58:23.262831Z","iopub.status.idle":"2023-02-23T14:58:23.547531Z","shell.execute_reply.started":"2023-02-23T14:58:23.262787Z","shell.execute_reply":"2023-02-23T14:58:23.546436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Invoking the TSNE method\ntsne = TSNE(n_components=2)\nX_tsne = tsne.fit_transform(X_train)","metadata":{"execution":{"iopub.status.busy":"2023-02-21T18:44:44.180301Z","iopub.execute_input":"2023-02-21T18:44:44.180776Z","iopub.status.idle":"2023-02-21T18:51:17.353061Z","shell.execute_reply.started":"2023-02-21T18:44:44.180737Z","shell.execute_reply":"2023-02-21T18:51:17.351850Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"It take some time to do it, but now we can plot in 2D our data and look how it is distributed across axis.","metadata":{}},{"cell_type":"code","source":"# Plot the result of TSNE reduction\nplt.figure(figsize=(12,5))\nplt.scatter(X_tsne[:500, 0], X_tsne[:500, 1], c=y_train[:500], alpha=0.7)\nfor i in range(500):\n    plt.text(X_tsne[i, 0], X_tsne[i, 1], str(y_train[i]))\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-02-21T19:04:02.807724Z","iopub.execute_input":"2023-02-21T19:04:02.808112Z","iopub.status.idle":"2023-02-21T19:04:04.440536Z","shell.execute_reply.started":"2023-02-21T19:04:02.808081Z","shell.execute_reply":"2023-02-21T19:04:04.439198Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Here we can see that the result is pretty good. Number like 0 and 6 are close to each other and far from number like 1.  \nEven if we have taken only the 500 first images, there is almost no overlaping between the different groups. It shows how powerfull is this method (TSNE).\n\nMoreover, we can see that it looks like possible to reduce the dimensionality of our dataset, to ensure good and fast performance with classical machine learning methods, and avoid overfitting.","metadata":{}},{"cell_type":"markdown","source":"# Dimensionality Reduction Using PCA","metadata":{}},{"cell_type":"code","source":" # Detection of the number of component needed\npca = PCA()\npca.fit(X_train)\n\n# Explained variance ratio analysis\nexp_var = pca.explained_variance_ratio_\ncumsum_exp_var = np.cumsum(exp_var)\n\nplt.figure(figsize=(12,5))\nplt.bar(\n    range(0, len(exp_var)), \n    exp_var, \n    label='Individual explained variance', \n    color='C0'\n)\nplt.step(\n    range(0, len(cumsum_exp_var)), \n    cumsum_exp_var, \n    label='Cumulative explained variance', \n    color='C1'\n)\nplt.scatter(\n    np.argmax(np.cumsum(exp_var) > 0.95), \n    0.95, \n    label='Threshold to save 95% of the initial variance', \n    c='C3',\n    zorder=3\n)\nplt.xlabel(\"Principal component index\")\nplt.ylabel(\"Explained variance ratio\")\nplt.legend()\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-02-21T22:57:50.481715Z","iopub.execute_input":"2023-02-21T22:57:50.482931Z","iopub.status.idle":"2023-02-21T22:57:55.731066Z","shell.execute_reply.started":"2023-02-21T22:57:50.482878Z","shell.execute_reply":"2023-02-21T22:57:55.729610Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see here thanks to this graphic, that choosing a number of components arround 154 will preserve 95% of our initial variance.","metadata":{}},{"cell_type":"code","source":"# Define and train PCA with 154 components (preserved 95% of the variance)\npca = PCA(n_components=154)\npca.fit(X_train)\n\n# Transform our train and test data\nX_train_reduced = pca.transform(X_train)\nX_test_reduced = pca.transform(X_test)","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:58:37.129486Z","iopub.execute_input":"2023-02-23T14:58:37.129979Z","iopub.status.idle":"2023-02-23T14:58:42.602251Z","shell.execute_reply.started":"2023-02-23T14:58:37.129926Z","shell.execute_reply":"2023-02-23T14:58:42.600484Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"X_train_recovered = pca.inverse_transform(X_train_reduced)\n\ncompare_images(X_train, X_train_recovered, y_train)","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:59:05.096825Z","iopub.execute_input":"2023-02-23T14:59:05.097308Z","iopub.status.idle":"2023-02-23T14:59:05.753035Z","shell.execute_reply.started":"2023-02-23T14:59:05.097266Z","shell.execute_reply":"2023-02-23T14:59:05.751438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see that there is a sligth image quality loss, but the digit is still mostly intact.","metadata":{}},{"cell_type":"markdown","source":"# Modeling","metadata":{}},{"cell_type":"markdown","source":"In this part we will use SVM (Support Vector Machine). SVM are powerful machine learning model, capable of performing linear or nonlinear classification, regression or even outlier detection.\n\nStratified cross validation (or StratifiedKFold) is a variation of the base cross validation technique avaliable in sklearn (KFold), which return stratified fold: each set contains approximately the same percentage of samples of each target class as the complete set. So StratifiedKFold  keeps the class proportions the same across all of the folds, which is vital for maintaining a representative subset of our data set.","metadata":{}},{"cell_type":"code","source":"# Define the StratifiedKFold\nskf = StratifiedKFold(n_splits=5)","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:59:44.659709Z","iopub.execute_input":"2023-02-23T14:59:44.660873Z","iopub.status.idle":"2023-02-23T14:59:44.665643Z","shell.execute_reply.started":"2023-02-23T14:59:44.660832Z","shell.execute_reply":"2023-02-23T14:59:44.664462Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can visualize the different folds on each class of our data. To do that, we have to sort the data by class, since it is shuffle (for a better visualization).  \nThe split method from StratifiedKFold generate indices to split data into training and test set. We can use this indices to create a figure.","metadata":{}},{"cell_type":"code","source":"# Visualize the Stratified cross validation\n# Sort indices by class\nsorted_indices = np.argsort(y_train)\n\n# Sort y_train and X_train by class\ny_train_sorted = y_train[sorted_indices]\nX_train_sorted = X_train_reduced[sorted_indices]\n\n# Plot\nplt.figure(figsize=(12, 5))\nfor i, (tr_idx, te_idx) in enumerate(skf.split(X_train_sorted, y_train_sorted), start=1):\n    # Fill in indices with the training/test groups\n    indices = np.zeros(len(X_train_sorted))\n    indices[te_idx] = 1\n\n    plt.scatter(\n        range(len(indices)), \n        [i] * len(indices),\n        c=indices,\n        marker=\"_\",\n        lw=10\n    )\nplt.scatter(\n    range(len(indices)),\n    [0] * len(indices), \n    c=y_train_sorted,\n    marker=\"_\", \n    lw=10\n)\nplt.ylabel('Fold')\nplt.xlabel('Rows')\n# Change the yticks to add class\nplt.yticks(ticks=range(6), labels=['class'] + list(range(1,6)))\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-02-23T14:59:51.838797Z","iopub.execute_input":"2023-02-23T14:59:51.840056Z","iopub.status.idle":"2023-02-23T14:59:53.952201Z","shell.execute_reply.started":"2023-02-23T14:59:51.839997Z","shell.execute_reply":"2023-02-23T14:59:53.950918Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see that for each class, there is 5 folds used as test. It keeps the class proportions the same across all of the folds, which is vital for maintaining a representative subset of our data set as said before.  \nIt is important to maintain a representative subset of our data set during the training, inp particular to avoid overfitting.\n\nNow we will train a SVC model on only one train and one validation set, to have a first impression of the performance of a support vector machines model. By default, the kernel of the SVC is set to 'rbf', which can handle nonlinearity, so we will use the default SVC model provided by Scikit-learn.","metadata":{}},{"cell_type":"code","source":"# First try using non-parametrize SVC model\n# And using only one validation set\n(X_strain, # 'strain' is used here for smaller train\n X_val,\n y_strain,\n y_val) = train_test_split(X_train_reduced, y_train, test_size=0.3, random_state=123)\n\n# Define SVC\nmodel = SVC()\nmodel.fit(X_strain, y_strain)\n\n# Display the score on the train\nmodel.score(X_strain, y_strain)","metadata":{"execution":{"iopub.status.busy":"2023-02-23T15:06:25.254782Z","iopub.execute_input":"2023-02-23T15:06:25.255269Z","iopub.status.idle":"2023-02-23T15:07:57.876338Z","shell.execute_reply.started":"2023-02-23T15:06:25.255232Z","shell.execute_reply":"2023-02-23T15:07:57.875159Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"The score on train data is very good: 99%. We can see the result on the validation set, by predicting and compare each prediction with the true value.","metadata":{}},{"cell_type":"code","source":"# Check prediction on the validation test\ny_spred = model.predict(X_val)\n\n# Display accuracy score\nprint(f\"accuracy: {accuracy_score(y_val, y_spred)}\\n\")\n\n# Confusion matrix\nplt.figure(figsize=(8,8))\nsb.heatmap(confusion_matrix(y_val, y_spred), annot=True, fmt = 'g')\nplt.xlabel(\"Reality\")\nplt.ylabel(\"Model prediction\")\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-02-23T15:12:07.611985Z","iopub.execute_input":"2023-02-23T15:12:07.613271Z","iopub.status.idle":"2023-02-23T15:12:34.938178Z","shell.execute_reply.started":"2023-02-23T15:12:07.613215Z","shell.execute_reply":"2023-02-23T15:12:34.936698Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Again, we have a very good score: 97%, which means that we are not in overfitting (and not in underfitting). But to be sure that this score is not due to the random split of our train and validation set, we will use stratified cross validation to analyze the model performance.\n\nIf the results of the cross validation are good, we will keep the model to predict the test set.","metadata":{}},{"cell_type":"code","source":"# Use stratified cross validation to avoid overfitting\nresult = pd.DataFrame(cross_validate(\n    SVC(),\n    X_train_reduced,\n    y_train,\n    return_train_score=True,\n    cv=skf.split(X_train_reduced, y_train),\n    scoring=['accuracy']\n))\nresult","metadata":{"execution":{"iopub.status.busy":"2023-02-23T15:17:17.023430Z","iopub.execute_input":"2023-02-23T15:17:17.023933Z","iopub.status.idle":"2023-02-23T15:28:30.300988Z","shell.execute_reply.started":"2023-02-23T15:17:17.023898Z","shell.execute_reply":"2023-02-23T15:28:30.299661Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We have a the details of the train and test prediction for the different folds here. So can analyze the total time of the stratified cross validation and above all, compare the train and test score of the different folds.","metadata":{}},{"cell_type":"code","source":"# Total cross validation time\ntime_fit = result['fit_time'].sum() \ntime_score = result['score_time'].sum()\nprint(f\"Total run time: {round(time_fit + time_score)} seconds\")","metadata":{"execution":{"iopub.status.busy":"2023-02-23T15:29:22.998491Z","iopub.execute_input":"2023-02-23T15:29:22.999028Z","iopub.status.idle":"2023-02-23T15:29:23.006860Z","shell.execute_reply.started":"2023-02-23T15:29:22.998981Z","shell.execute_reply":"2023-02-23T15:29:23.005754Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can see that the stratified cross validation process take more than 4 minutes here, that is reasonable amount of time.","metadata":{}},{"cell_type":"code","source":"train_df = pd.DataFrame({'accuracy' : result['train_accuracy'],\n                        'result' : ['Train'] * 5})\ntest_df = pd.DataFrame({'accuracy' : result['test_accuracy'],\n                        'result' : ['Test'] * 5})\n\nmodel_df = train_df.append(test_df)\n\nplt.figure(figsize = (12,5))\nsb.boxplot(x = 'result', y = 'accuracy', data = model_df)\nsb.stripplot(x = 'result', y = 'accuracy', data = model_df, jitter = True, color = 'black')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2023-02-23T15:29:27.510406Z","iopub.execute_input":"2023-02-23T15:29:27.511723Z","iopub.status.idle":"2023-02-23T15:29:27.742534Z","shell.execute_reply.started":"2023-02-23T15:29:27.511668Z","shell.execute_reply":"2023-02-23T15:29:27.741317Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"This graph confirm us that the model perform well, and we are not in overfitting, and our result does not depends of the train/validation split.\n\nNow we can predict the result for the test set. And replace the label value in the submission dataframe, convert it to a csv file, submit it.","metadata":{}},{"cell_type":"code","source":"# Prediction\ny_pred = model.predict(X_test_reduced)\ny_pred.shape","metadata":{"execution":{"iopub.status.busy":"2023-02-23T15:29:56.443839Z","iopub.execute_input":"2023-02-23T15:29:56.445168Z","iopub.status.idle":"2023-02-23T15:31:03.465705Z","shell.execute_reply.started":"2023-02-23T15:29:56.445112Z","shell.execute_reply":"2023-02-23T15:31:03.464516Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"We can verify one prediction using the image and the preditec value.","metadata":{}},{"cell_type":"code","source":"# Example of prediction\npredict_image(X_test, y_pred)","metadata":{"execution":{"iopub.status.busy":"2023-02-23T15:32:10.206546Z","iopub.execute_input":"2023-02-23T15:32:10.207919Z","iopub.status.idle":"2023-02-23T15:32:10.401621Z","shell.execute_reply.started":"2023-02-23T15:32:10.207855Z","shell.execute_reply":"2023-02-23T15:32:10.400138Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# Replace the label (all 0) with the prediction\ndf_submission['Label'] = y_pred\ndf_submission['Label'].value_counts()","metadata":{"execution":{"iopub.status.busy":"2023-02-23T15:32:17.089356Z","iopub.execute_input":"2023-02-23T15:32:17.089852Z","iopub.status.idle":"2023-02-23T15:32:17.102311Z","shell.execute_reply.started":"2023-02-23T15:32:17.089816Z","shell.execute_reply":"2023-02-23T15:32:17.100771Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"So our work is ended. We just have to save our result in a csv file and submit this file to be analyzed and scored by Kaggle.","metadata":{}},{"cell_type":"code","source":"# Save as csv the result\ndf_submission.to_csv('submission.csv', index=False)\n!head submission.csv # Use Linux command to show the first row of our file","metadata":{"execution":{"iopub.status.busy":"2023-02-23T15:32:43.054159Z","iopub.execute_input":"2023-02-23T15:32:43.055246Z","iopub.status.idle":"2023-02-23T15:32:44.200531Z","shell.execute_reply.started":"2023-02-23T15:32:43.055195Z","shell.execute_reply":"2023-02-23T15:32:44.199239Z"},"trusted":true},"execution_count":null,"outputs":[]}]}