riyageorge commited on
Commit
e1bb13a
·
1 Parent(s): 37bc6d8

Upload 3 files

Browse files
dnn_smsspam_model.h5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9194959640c90b0579ead07653f3ebc4ddea231a1acd28a18a5b6a7b96b5b821
3
+ size 5890160
dnn_smsspam_model.ipynb ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "### SMS SPAM DETECTION USING DNN"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "code",
12
+ "execution_count": 1,
13
+ "metadata": {},
14
+ "outputs": [],
15
+ "source": [
16
+ "import pandas as pd\n",
17
+ "import matplotlib.pyplot as plt\n",
18
+ "import seaborn as sns\n",
19
+ "from sklearn.model_selection import train_test_split\n",
20
+ "import tensorflow as tf\n",
21
+ "from sklearn.metrics import classification_report, confusion_matrix, accuracy_score\n",
22
+ "import pickle"
23
+ ]
24
+ },
25
+ {
26
+ "cell_type": "code",
27
+ "execution_count": 2,
28
+ "metadata": {},
29
+ "outputs": [],
30
+ "source": [
31
+ "# Downloading Dataset\n",
32
+ "dataset = pd.read_csv(r'SMSSpamCollection.txt', sep='\\t', names=['label', 'message'])"
33
+ ]
34
+ },
35
+ {
36
+ "cell_type": "code",
37
+ "execution_count": 3,
38
+ "metadata": {},
39
+ "outputs": [
40
+ {
41
+ "name": "stdout",
42
+ "output_type": "stream",
43
+ "text": [
44
+ " label message\n",
45
+ "0 ham Go until jurong point, crazy.. Available only ...\n",
46
+ "1 ham Ok lar... Joking wif u oni...\n",
47
+ "2 spam Free entry in 2 a wkly comp to win FA Cup fina...\n",
48
+ "3 ham U dun say so early hor... U c already then say...\n",
49
+ "4 ham Nah I don't think he goes to usf, he lives aro...\n",
50
+ "---------------------- -------------------------\n",
51
+ " message \n",
52
+ " count unique top freq\n",
53
+ "label \n",
54
+ "ham 4825 4516 Sorry, I'll call later 30\n",
55
+ "spam 747 653 Please call our customer service representativ... 4\n"
56
+ ]
57
+ }
58
+ ],
59
+ "source": [
60
+ "print(dataset.head())\n",
61
+ "print(\"---------------------- -------------------------\")\n",
62
+ "print(dataset.groupby('label').describe())"
63
+ ]
64
+ },
65
+ {
66
+ "cell_type": "code",
67
+ "execution_count": 4,
68
+ "metadata": {},
69
+ "outputs": [],
70
+ "source": [
71
+ "# Preprocessing\n",
72
+ "dataset['label'] = dataset['label'].map({'spam': 1, 'ham': 0})\n",
73
+ "X = dataset['message'].values\n",
74
+ "y = dataset['label'].values"
75
+ ]
76
+ },
77
+ {
78
+ "cell_type": "code",
79
+ "execution_count": 5,
80
+ "metadata": {},
81
+ "outputs": [
82
+ {
83
+ "name": "stdout",
84
+ "output_type": "stream",
85
+ "text": [
86
+ "[[387, 245, 325, 450, 917, 432, 1, 1323, 169, 2377], [19, 4, 1021, 112, 93, 6, 40, 358]]\n"
87
+ ]
88
+ }
89
+ ],
90
+ "source": [
91
+ "# Train Test Split\n",
92
+ "X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)\n",
93
+ "\n",
94
+ "tokeniser = tf.keras.preprocessing.text.Tokenizer()\n",
95
+ "tokeniser.fit_on_texts(X_train)\n",
96
+ "\n",
97
+ "# Save the tokenizer using pickle\n",
98
+ "with open('dnn_smsspam_tokenizer.pickle', 'wb') as handle:\n",
99
+ " pickle.dump(tokeniser, handle, protocol=pickle.HIGHEST_PROTOCOL)\n",
100
+ "\n",
101
+ "encoded_train = tokeniser.texts_to_sequences(X_train)\n",
102
+ "encoded_test = tokeniser.texts_to_sequences(X_test)\n",
103
+ "print(encoded_train[0:2])"
104
+ ]
105
+ },
106
+ {
107
+ "cell_type": "code",
108
+ "execution_count": 6,
109
+ "metadata": {},
110
+ "outputs": [
111
+ {
112
+ "name": "stdout",
113
+ "output_type": "stream",
114
+ "text": [
115
+ "[[ 14 61 388 540 3557 23 3558 0 0 0 0 0 0 0\n",
116
+ " 0 0 0 0 0 0]\n",
117
+ " [ 474 59 35 10 61 22 63 75 76 0 0 0 0 0\n",
118
+ " 0 0 0 0 0 0]\n",
119
+ " [ 36 727 180 26 3559 2396 452 41 9 1850 0 0 0 0\n",
120
+ " 0 0 0 0 0 0]\n",
121
+ " [ 518 2397 158 73 243 10 48 92 0 0 0 0 0 0\n",
122
+ " 0 0 0 0 0 0]]\n"
123
+ ]
124
+ }
125
+ ],
126
+ "source": [
127
+ "# Padding\n",
128
+ "max_length = 20\n",
129
+ "padded_train = tf.keras.preprocessing.sequence.pad_sequences(encoded_train, maxlen=max_length, padding='post')\n",
130
+ "padded_test = tf.keras.preprocessing.sequence.pad_sequences(encoded_test, maxlen=max_length, padding='post')\n",
131
+ "print(padded_train[30:34])"
132
+ ]
133
+ },
134
+ {
135
+ "cell_type": "code",
136
+ "execution_count": 7,
137
+ "metadata": {},
138
+ "outputs": [],
139
+ "source": [
140
+ "vocab_size = len(tokeniser.word_index) + 1"
141
+ ]
142
+ },
143
+ {
144
+ "cell_type": "code",
145
+ "execution_count": 8,
146
+ "metadata": {},
147
+ "outputs": [],
148
+ "source": [
149
+ "# Model definition\n",
150
+ "model=tf.keras.models.Sequential([\n",
151
+ " tf.keras.layers.Embedding(input_dim=vocab_size,output_dim= 64, input_length=max_length),\n",
152
+ " tf.keras.layers.GlobalAveragePooling1D(),\n",
153
+ " tf.keras.layers.Dense(64, activation='relu'),\n",
154
+ " tf.keras.layers.Dense(32, activation='relu'),\n",
155
+ " tf.keras.layers.Dense(16, activation='relu'),\n",
156
+ " tf.keras.layers.Dense(1, activation='sigmoid')\n",
157
+ "])"
158
+ ]
159
+ },
160
+ {
161
+ "cell_type": "code",
162
+ "execution_count": 9,
163
+ "metadata": {},
164
+ "outputs": [
165
+ {
166
+ "name": "stdout",
167
+ "output_type": "stream",
168
+ "text": [
169
+ "Model: \"sequential\"\n",
170
+ "_________________________________________________________________\n",
171
+ " Layer (type) Output Shape Param # \n",
172
+ "=================================================================\n",
173
+ " embedding (Embedding) (None, 20, 64) 480128 \n",
174
+ " \n",
175
+ " global_average_pooling1d ( (None, 64) 0 \n",
176
+ " GlobalAveragePooling1D) \n",
177
+ " \n",
178
+ " dense (Dense) (None, 64) 4160 \n",
179
+ " \n",
180
+ " dense_1 (Dense) (None, 32) 2080 \n",
181
+ " \n",
182
+ " dense_2 (Dense) (None, 16) 528 \n",
183
+ " \n",
184
+ " dense_3 (Dense) (None, 1) 17 \n",
185
+ " \n",
186
+ "=================================================================\n",
187
+ "Total params: 486913 (1.86 MB)\n",
188
+ "Trainable params: 486913 (1.86 MB)\n",
189
+ "Non-trainable params: 0 (0.00 Byte)\n",
190
+ "_________________________________________________________________\n",
191
+ "None\n"
192
+ ]
193
+ }
194
+ ],
195
+ "source": [
196
+ "# compile the model\n",
197
+ "model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])\n",
198
+ "\n",
199
+ "# summarize the model\n",
200
+ "print(model.summary())\n",
201
+ "\n",
202
+ "# Early stopping callback\n",
203
+ "early_stop = tf.keras.callbacks.EarlyStopping(monitor='accuracy', mode='min', patience=10)"
204
+ ]
205
+ },
206
+ {
207
+ "cell_type": "code",
208
+ "execution_count": 10,
209
+ "metadata": {},
210
+ "outputs": [
211
+ {
212
+ "name": "stdout",
213
+ "output_type": "stream",
214
+ "text": [
215
+ "Epoch 1/50\n",
216
+ "122/122 [==============================] - 2s 6ms/step - loss: 0.3687 - accuracy: 0.8895 - val_loss: 0.0994 - val_accuracy: 0.9767\n",
217
+ "Epoch 2/50\n",
218
+ "122/122 [==============================] - 1s 4ms/step - loss: 0.0500 - accuracy: 0.9864 - val_loss: 0.0381 - val_accuracy: 0.9904\n",
219
+ "Epoch 3/50\n",
220
+ "122/122 [==============================] - 1s 5ms/step - loss: 0.0163 - accuracy: 0.9959 - val_loss: 0.0373 - val_accuracy: 0.9910\n",
221
+ "Epoch 4/50\n",
222
+ "122/122 [==============================] - 1s 5ms/step - loss: 0.0069 - accuracy: 0.9985 - val_loss: 0.0399 - val_accuracy: 0.9886\n",
223
+ "Epoch 5/50\n",
224
+ "122/122 [==============================] - 1s 5ms/step - loss: 0.0043 - accuracy: 0.9992 - val_loss: 0.0416 - val_accuracy: 0.9910\n",
225
+ "Epoch 6/50\n",
226
+ "122/122 [==============================] - 1s 6ms/step - loss: 0.0026 - accuracy: 0.9995 - val_loss: 0.0439 - val_accuracy: 0.9910\n",
227
+ "Epoch 7/50\n",
228
+ "122/122 [==============================] - 1s 5ms/step - loss: 0.0018 - accuracy: 0.9997 - val_loss: 0.0454 - val_accuracy: 0.9910\n",
229
+ "Epoch 8/50\n",
230
+ "122/122 [==============================] - 1s 5ms/step - loss: 0.0011 - accuracy: 0.9997 - val_loss: 0.0476 - val_accuracy: 0.9916\n",
231
+ "Epoch 9/50\n",
232
+ "122/122 [==============================] - 1s 6ms/step - loss: 0.0015 - accuracy: 0.9992 - val_loss: 0.0533 - val_accuracy: 0.9904\n",
233
+ "Epoch 10/50\n",
234
+ "122/122 [==============================] - 1s 5ms/step - loss: 2.8591e-04 - accuracy: 1.0000 - val_loss: 0.0531 - val_accuracy: 0.9910\n",
235
+ "Epoch 11/50\n",
236
+ "122/122 [==============================] - 1s 5ms/step - loss: 3.3040e-04 - accuracy: 1.0000 - val_loss: 0.0553 - val_accuracy: 0.9904\n"
237
+ ]
238
+ },
239
+ {
240
+ "data": {
241
+ "text/plain": [
242
+ "<keras.src.callbacks.History at 0x252ee469930>"
243
+ ]
244
+ },
245
+ "execution_count": 10,
246
+ "metadata": {},
247
+ "output_type": "execute_result"
248
+ }
249
+ ],
250
+ "source": [
251
+ "# Model training\n",
252
+ "model.fit(x=padded_train,\n",
253
+ " y=y_train,\n",
254
+ " epochs=50,\n",
255
+ " validation_data=(padded_test, y_test),\n",
256
+ " callbacks=[early_stop]\n",
257
+ " )"
258
+ ]
259
+ },
260
+ {
261
+ "cell_type": "code",
262
+ "execution_count": 11,
263
+ "metadata": {},
264
+ "outputs": [
265
+ {
266
+ "name": "stdout",
267
+ "output_type": "stream",
268
+ "text": [
269
+ "53/53 [==============================] - 0s 886us/step\n"
270
+ ]
271
+ }
272
+ ],
273
+ "source": [
274
+ "# Generate predictions after model training\n",
275
+ "preds = (model.predict(padded_test) > 0.5).astype(\"int32\")"
276
+ ]
277
+ },
278
+ {
279
+ "cell_type": "code",
280
+ "execution_count": 12,
281
+ "metadata": {},
282
+ "outputs": [
283
+ {
284
+ "name": "stdout",
285
+ "output_type": "stream",
286
+ "text": [
287
+ "Classification Report\n",
288
+ " precision recall f1-score support\n",
289
+ "\n",
290
+ " 0 0.99 1.00 0.99 1448\n",
291
+ " 1 1.00 0.93 0.96 224\n",
292
+ "\n",
293
+ " accuracy 0.99 1672\n",
294
+ " macro avg 0.99 0.97 0.98 1672\n",
295
+ "weighted avg 0.99 0.99 0.99 1672\n",
296
+ "\n",
297
+ "Accuracy : 99.04\n"
298
+ ]
299
+ }
300
+ ],
301
+ "source": [
302
+ "# Classification report\n",
303
+ "print(\"Classification Report\")\n",
304
+ "print(classification_report(y_test, preds))\n",
305
+ "\n",
306
+ "# Accuracy score\n",
307
+ "acc_sc = accuracy_score(y_test, preds)\n",
308
+ "print(f\"Accuracy : {round(acc_sc * 100, 2)}\")"
309
+ ]
310
+ },
311
+ {
312
+ "cell_type": "code",
313
+ "execution_count": 13,
314
+ "metadata": {},
315
+ "outputs": [
316
+ {
317
+ "data": {
318
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAAi8AAAGwCAYAAABhDIVPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAhKUlEQVR4nO3deXRUZb6v8W8RSGUkgoxBCGBkahlDQzODIHDtwyCnm3MU7YCCLcggEYS0h1kIDSIYmRSQAAcFLgoNqK2IMggIEoGrEmKHGQSUE4ZOgEy1zx9cqi0yWEUqqbzh+ayVtay9d3b9ShfyrF1v1bZZlmUJAADAEGV8PQAAAIAniBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUcr6eoCiEth8uK9HAFAEbhycr5vZvp4CQFEIcLNKuPICAACMQrwAAACjEC8AAMAoxAsAADAK8QIAAIxCvAAAAKMQLwAAwCjECwAAMArxAgAAjEK8AAAAoxAvAADAKMQLAAAwCvECAACMQrwAAACjEC8AAMAoxAsAADAK8QIAAIxCvAAAAKMQLwAAwCjECwAAMArxAgAAjEK8AAAAoxAvAADAKMQLAAAwCvECAACMQrwAAACjEC8AAMAoxAsAADAK8QIAAIxCvAAAAKMQLwAAwCjECwAAMArxAgAAjEK8AAAAoxAvAADAKMQLAAAwCvECAACMQrwAAACjEC8AAMAoxAsAADAK8QIAAIxCvAAAAKMQLwAAwCjECwAAMArxAgAAjEK8AAAAoxAvAADAKMQLAAAwCvECAACMQrwAAACjEC8AAMAoxAsAADAK8QIAAIxCvAAAAKMQLwAAwCjECwAAMArxAgAAjEK8AAAAoxAvAADAKMQLAAAwCvECAACMQrwAAACjEC8AAMAoxAsAADAK8QIAAIxCvAAAAKMQLwAAwCjECwAAMArxAgAAjEK8AAAAoxAvAADAKMQLAAAwCvECAACMQrwAAACjEC8AAMAoxAsAADAK8QIAAIxCvAAAAKMQLwAAwCjECwAAMArxAgAAjEK8AAAAoxAvAADAKMQLAAAwCvECAACMQrzA59q1eFDr5/1Zxz+drhsH56tX5yb5Hhv/yn/qxsH5Gv5k5zz3+5crq6/WjNeNg/PVpF4N5/ZX/vyYbhycn+vn0p453n45AAop8cDXGjHseXXr3F5Nf1Nfn2/7zNcjoYQhXuBzwYF2ffvDOb0Yt7bA43p3aaJWjWvrx5+u5HvMjBf76PzPV3Ntn7fyM9XuFuvyc+TYeX2w9WBhxwfgZTduXFf9+vUV+1+TfD0KSqiyvh4A+HT3EX26+0iBx4RXDtPr4/6oXsMWaMObQ/M8pnu7Rur6u4Z6YuxS9Wz/G5d96TcylX4j0/m4cb0aavRgdY2cvqbwLwCAV7Xv0EntO3Ty9RgowYgXlHg2m03LXv2T5q7YpqTjF/I8pkrFUC2c8IT6xyzR9V9ESn4GPd5WP5y8qN0Hj3l7XABAEfNpvFy6dEnvvPOO9u7dqwsXbv2lVK1aNbVt21YDBw5U5cqVfTkeSoiXBj2q7ByHFry3Pd9j3p76lJas/1LfHDmtWtUrFng+u39Z/cf/aak5y7d6eVIAQHHwWbx8/fXX6tGjh4KCgtStWzfVq1dPknTx4kXFx8dr5syZ+uSTT9SyZcsCz5ORkaGMjAyXbXa7vcjmRvFq3rCmXniis9o++dd8jxn2RCeFBgVo9jufunXOPo80VWhQgP578z5vjQkAKEY+i5cRI0boj3/8oxYvXiybzeayz7IsPf/88xoxYoT27t1b4Hni4uI0ZcoUl22TJrHIq7Ro1/xBVakYoh8+murcVrasn2bG9NPwAV3U4PeT1Pm39dS6SR1d3TfP5Xd3r35Zaz4+oCETV7lsH9i3rT7e9Z1+Sv1ncbwEAICX+SxeDh8+rISEhFzhIt1a4zB69Gg1b978V88TGxurmJgYl212u11//dtLXpsVvvPuh1/r833JLts2L3xB7364Xyv/9pUk6aVZ6zV5wRbn/uqVw7Rl0XA9PX65vv72pMvvRoTfr06/fUh/ePHtIp8dAFA0fBYv1apV0/79+9WgQYM89+/fv19Vq1b91fPY7XbeJjJccKC/Hqz5r/VNtWvcryb1aujytes6c+GyUq+muxyflZ2ji5eu6R+nfpIknblw2WV/2vVbbyMeP/Ozzt3xserovr/ThUvX9Mnu74vglQDwhuvp6Tp9+rTz8bmzZ3U0KUlhYWGqHh7uw8lQUvgsXsaMGaPnnntOiYmJ6tq1qzNULl68qG3btmnJkiV67bXXfDUeilGLRhH6dOko5+NZY/5dkrRq01d6btJ/e+15bDabnu71O63atE8Oh+W18wLwru+//06DB/3J+fi1WXGSpN59Hte0GTN9NRZKEJtlWT77v/jatWs1d+5cJSYmKicnR5Lk5+enqKgoxcTEqH///nd97sDmw701JoAS5MbB+bqZ7espABSFADcvqfg0Xm7LysrSpUuXJEmVKlVSuXLlCn1O4gUonYgXoPRyN15KxJfUlStXTtWrV/f1GAAAwADc2wgAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARinrzkGbNm1y+4S9e/e+62EAAAB+jVvx0rdvX7dOZrPZlJOTU5h5AAAACuRWvDgcjqKeAwAAwC2FWvNy8+ZNb80BAADgFo/jJScnR9OmTVONGjUUEhKi48ePS5ImTJigZcuWeX1AAACAX/I4XqZPn66EhATNmjVL/v7+zu0PP/ywli5d6tXhAAAA7uRxvKxcuVJvv/22BgwYID8/P+f2pk2b6ujRo14dDgAA4E4ex8u5c+cUGRmZa7vD4VBWVpZXhgIAAMiPx/HSqFEj7dq1K9f29evXq3nz5l4ZCgAAID9ufVT6lyZOnKjo6GidO3dODodDH3zwgZKTk7Vy5Upt2bKlKGYEAABw8vjKS58+fbR582Z99tlnCg4O1sSJE5WUlKTNmzfr0UcfLYoZAQAAnGyWZVm+HqIoBDYf7usRABSBGwfn62a2r6cAUBQC3Hw/yOO3jW47cOCAkpKSJN1aBxMVFXW3pwIAAHCbx/Fy9uxZPfHEE9q9e7fuu+8+SdKVK1fUtm1brVmzRg888IC3ZwQAAHDyeM3L4MGDlZWVpaSkJKWmpio1NVVJSUlyOBwaPHhwUcwIAADg5PGal8DAQO3ZsyfXx6ITExPVoUMHXb9+3asD3i3WvAClE2tegNLL3TUvHl95qVmzZp5fRpeTk6Pw8HBPTwcAAOARj+Nl9uzZGjFihA4cOODcduDAAY0aNUqvvfaaV4cDAAC4k1tvG1WoUEE2m835OD09XdnZ2Spb9tb1ndv/HBwcrNTU1KKb1gO8bQSUTrxtBJReXv2o9Lx58woxCgAAgPe4FS/R0dFFPQcAAIBb7vpL6iTp5s2byszMdNlWvnz5Qg0EAABQEI8X7Kanp2v48OGqUqWKgoODVaFCBZcfAACAouRxvLz88sv6/PPPtWjRItntdi1dulRTpkxReHi4Vq5cWRQzAgAAOHn8ttHmzZu1cuVKde7cWYMGDVKHDh0UGRmpiIgIrV69WgMGDCiKOQEAACTdxZWX1NRU1a1bV9Kt9S23Pxrdvn177dy507vTAQAA3MHjeKlbt65OnDghSWrQoIHWrVsn6dYVmds3agQAACgqHsfLoEGDdPjwYUnS+PHjtWDBAgUEBGj06NEaO3as1wcEAAD4JY9vzHinU6dOKTExUZGRkWrSpIm35io0vmEXKJ34hl2g9PLqN+wWJCIiQhEREYU9DQAAgFvcipf4+Hi3Tzhy5Mi7HgYAAODXuPW2UZ06ddw7mc2m48ePF3ooAACA/BR6zUtJdT2rVL4s4J4XVM6mlJ9u+HoMAEUgskqgW8d5/GkjAAAAXyJeAACAUYgXAABgFOIFAAAYhXgBAABGuat42bVrl5566im1adNG586dkyStWrVKX375pVeHAwAAuJPH8fL++++rR48eCgwM1MGDB5WRkSFJunr1qmbMmOH1AQEAAH7J43h59dVXtXjxYi1ZskTlypVzbm/Xrp2++eYbrw4HAABwJ4/jJTk5WR07dsy1PSwsTFeuXPHGTAAAAPnyOF6qVaumlJSUXNu//PJL1a1b1ytDAQAA5MfjeBkyZIhGjRqlffv2yWaz6ccff9Tq1as1ZswYDR06tChmBAAAcHLrrtK/NH78eDkcDnXt2lXXr19Xx44dZbfbNWbMGI0YMaIoZgQAAHC66xszZmZmKiUlRWlpaWrUqJFCQkK8PVuhcGNGoHTixoxA6eXujRk9vvJym7+/vxo1anS3vw4AAHBXPI6XLl26yGaz5bv/888/L9RAAAAABfE4Xpo1a+byOCsrS4cOHdJ3332n6Ohob80FAACQJ4/jZe7cuXlunzx5stLS0go9EAAAQEHuesHunVJSUtSqVSulpqZ643SFxoJdoHRiwS5Qerm7YNdrd5Xeu3evAgICvHU6AACAPHn8tlG/fv1cHluWpfPnz+vAgQOaMGGC1wYDAADIi8fxEhYW5vK4TJkyql+/vqZOnaru3bt7bTAAAIC8eBQvOTk5GjRokBo3bqwKFSoU1UwAAAD58mjNi5+fn7p3787dowEAgM94vGD34Ycf1vHjx4tiFgAAgF/lcby8+uqrGjNmjLZs2aLz58/r2rVrLj8AAABFye3veZk6dapeeuklhYaG/uuXf3GbAMuyZLPZlJOT4/0p7wLf8wKUTnzPC1B6ufs9L27Hi5+fn86fP6+kpKQCj+vUqZNbT1zUiBegdCJegNLL63eVvt04JSVOAADAvcmjNS8F3U0aAACgOHj0PS/16tX71YApKfc2AgAApZNH8TJlypRc37ALAABQnNxesFumTBlduHBBVapUKeqZvIIFu0DpxIJdoPTy+l2lWe8CAABKArfjxc0LNAAAAEXK7TUvDoejKOcAAABwi8e3BwAAAPAl4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABilrK8HANyReOBrrVy+TEeOfK9LP/+s19+Yry5duzn3T3xlvDb/baPL77Rt114L3lpazJMCKMi6Vcu0Z+c2nT11Uv52uxo+3FSDhr6oB2rVdh6TmZGhpQvmaOe2T5SVlakWrdpqWMxfVKHi/c5jDh3Yp1XLFujUsRTZAwPVtWcvRQ8ZLr+y/LV2L+DKC4xw48YN1avfQLGvTMz3mLbtO2jr9l3On7hZc4pxQgDu+PZQon7/+H9ozlsr9ercxcrOztZ/xQzVzRs3nMcsefM17d+9U7FTZ2vmm8uUeulnTX8lxrn/eEqyJr08XFGt2in+nTUaP/mv2rd7h5a/Fe+LlwQfIFFhhPYdOqp9h44FHuPv769KlSoX00QA7sa0OQtdHsf8Zaqe7P2IUpKP6OFmUUpP+6c+/XCDxk6MU9OoVpKkF2On6PmnHtfR7/+fGvymiXZt+0R1HnxITw76syQp/IFaemboi5o58WU9OejPCgoKLvbXheLFlReUGge+3q9HOrZV33/rqelTJ+vKlcu+HgnAr0hPT5MkhZQPkySlJCcpOztbzVq2dh5TM6KOKletrqTvDkuSsrKy5O9vdzmPv92uzMwMpSQfKabJ4UvGX3nJyMhQRkaGyza73S6V8ffRRPCFtu066JFu3VWjRg2dPXNGb74xV8Off04rVq+Rn5+fr8cDkAeHw6G342erUeNmql03UpJ0OfWSypYrp5DQ8i7HVqhYUZdT/0eS1KJVG/3t/67W9s8+Vocu3XU59ZLeS3hbkpT6P5eK90XAJ0r0lZczZ87omWeeKfCYuLg4hYWFufzExcUV04QoKXo+9nt17vKIHqpXX126dlP8gsX6/rtvdeDr/b4eDUA+Fr0ep1MnUjRu8l89+r0WrdrqmaGjteC16erbtZWee7KPWv6uvSSpjK1E/7UGLynR/5VTU1O1YsWKAo+JjY3V1atXXX5iY2OLaUKUVA/UrKn7KlTQmdOnfD0KgDwsmhun/Xt3Ku6NpapUpapze4WKlZSdlaW0f15zOf5yaqrLp40e/8+nte7jXUpY/7He27Jdv2vfWZJULbxGscwP3/Lp20abNm0qcP/x48d/9Rx2u/3W20R3uJ5l3fVcMN/FCxd09coVVapcxdejAPgFy7K0eN5M7d35ueLil+aKjcj6DVW2bFkdTtyvdp1vfR3C2dMn9fPF82r4cFOXY202m+6vdOvP+I7P/q7KVarpwXoNi+eFwKd8Gi99+/aVzWaTZeUfGjabrRgnQkl1/Xq6zpw+7Xx87txZJR9NUvn//1bhWwsXqOuj3VWpUiWdOXNGb7w+WzVr1VLbdu19ODWAOy18fYZ2fPaxJsyYp8CgYOcaleCQENntAQoOCVX33z+uJfPnKKR8mIKCg7V43kw1eLiJGvymifM877+boKjW7WQrY9OeHZ9r/ep3NH7KLNa43SNsVkHlUMRq1KihhQsXqk+fPnnuP3TokKKiopSTk+PxubnyUroc2L9PQ56JzrW9V5+++suEyYoZ+YKOHk3SP6/9U5WrVFabtu00bPgo3V+pkg+mRVEKKmdTyk83fv1AlEi/79Asz+0vxk7Ro4/d+rvg9pfU7fjs7y5fUlfx/n/9eY4dNUTHfkhSVmaW6kTW05OD/uxc9wJzRVYJdOs4n8ZL79691axZM02dOjXP/YcPH1bz5s3lcDg8PjfxApROxAtQerkbLz5922js2LFKT0/Pd39kZKS++OKLYpwIAACUdD698lKUuPIClE5ceQFKL3evvJToj0oDAADciXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYhXgBAABGIV4AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAACAUYgXAABgFOIFAAAYxWZZluXrIYC7lZGRobi4OMXGxsput/t6HABexJ9v5Id4gdGuXbumsLAwXb16VeXLl/f1OAC8iD/fyA9vGwEAAKMQLwAAwCjECwAAMArxAqPZ7XZNmjSJxXxAKcSfb+SHBbsAAMAoXHkBAABGIV4AAIBRiBcAAGAU4gUAABiFeIHRFixYoNq1aysgIECtW7fW/v37fT0SgELauXOnevXqpfDwcNlsNm3cuNHXI6GEIV5grLVr1yomJkaTJk3SN998o6ZNm6pHjx766aeffD0agEJIT09X06ZNtWDBAl+PghKKj0rDWK1bt9Zvf/tbzZ8/X5LkcDhUs2ZNjRgxQuPHj/fxdAC8wWazacOGDerbt6+vR0EJwpUXGCkzM1OJiYnq1q2bc1uZMmXUrVs37d2714eTAQCKGvECI126dEk5OTmqWrWqy/aqVavqwoULPpoKAFAciBcAAGAU4gVGqlSpkvz8/HTx4kWX7RcvXlS1atV8NBUAoDgQLzCSv7+/oqKitG3bNuc2h8Ohbdu2qU2bNj6cDABQ1Mr6egDgbsXExCg6OlotW7ZUq1atNG/ePKWnp2vQoEG+Hg1AIaSlpSklJcX5+MSJEzp06JAqVqyoWrVq+XAylBR8VBpGmz9/vmbPnq0LFy6oWbNmio+PV+vWrX09FoBC2L59u7p06ZJre3R0tBISEop/IJQ4xAsAADAKa14AAIBRiBcAAGAU4gUAABiFeAEAAEYhXgAAgFGIFwAAYBTiBQAAGIV4AQAARiFeAHjdwIED1bdvX+fjzp0768UXXyz2ObZv3y6bzaYrV67ke4zNZtPGjRvdPufkyZPVrFmzQs118uRJ2Ww2HTp0qFDnAe5VxAtwjxg4cKBsNptsNpv8/f0VGRmpqVOnKjs7u8if+4MPPtC0adPcOtad4ABwb+PGjMA9pGfPnlq+fLkyMjL00Ucf6YUXXlC5cuUUGxub69jMzEz5+/t75XkrVqzolfMAgMSVF+CeYrfbVa1aNUVERGjo0KHq1q2bNm3aJOlfb/VMnz5d4eHhql+/viTpzJkz6t+/v+677z5VrFhRffr00cmTJ53nzMnJUUxMjO677z7df//9evnll3XnLdPufNsoIyND48aNU82aNWW32xUZGally5bp5MmTzhvyVahQQTabTQMHDpQkORwOxcXFqU6dOgoMDFTTpk21fv16l+f56KOPVK9ePQUGBqpLly4uc7pr3LhxqlevnoKCglS3bl1NmDBBWVlZuY576623VLNmTQUFBal///66evWqy/6lS5eqYcOGCggIUIMGDbRw4UKPZwGQN+IFuIcFBgYqMzPT+Xjbtm1KTk7W1q1btWXLFmVlZalHjx4KDQ3Vrl27tHv3boWEhKhnz57O35szZ44SEhL0zjvv6Msvv1Rqaqo2bNhQ4PP+6U9/0nvvvaf4+HglJSXprbfeUkhIiGrWrKn3339fkpScnKzz58/rjTfekCTFxcVp5cqVWrx4sb7//nuNHj1aTz31lHbs2CHpVmT169dPvXr10qFDhzR48GCNHz/e438noaGhSkhI0JEjR/TGG29oyZIlmjt3rssxKSkpWrdunTZv3qy///3vOnjwoIYNG+bcv3r1ak2cOFHTp09XUlKSZsyYoQkTJmjFihUezwMgDxaAe0J0dLTVp08fy7Isy+FwWFu3brXsdrs1ZswY5/6qVataGRkZzt9ZtWqVVb9+fcvhcDi3ZWRkWIGBgdYnn3xiWZZlVa9e3Zo1a5Zzf1ZWlvXAAw84n8uyLKtTp07WqFGjLMuyrOTkZEuStXXr1jzn/OKLLyxJ1uXLl53bbt68aQUFBVl79uxxOfbZZ5+1nnjiCcuyLCs2NtZq1KiRy/5x48blOtedJFkbNmzId//s2bOtqKgo5+NJkyZZfn5+1tmzZ53bPv74Y6tMmTLW+fPnLcuyrAcffNB69913Xc4zbdo0q02bNpZlWdaJEycsSdbBgwfzfV4A+WPNC3AP2bJli0JCQpSVlSWHw6Enn3xSkydPdu5v3LixyzqXw4cPKyUlRaGhoS7nuXnzpo4dO6arV6/q/Pnzat26tXNf2bJl1bJly1xvHd126NAh+fn5qVOnTm7PnZKSouvXr+vRRx912Z6ZmanmzZtLkpKSklzmkKQ2bdq4/Ry3rV27VvHx8Tp27JjS0tKUnZ2t8uXLuxxTq1Yt1ahRw+V5HA6HkpOTFRoaqmPHjunZZ5/VkCFDnMdkZ2crLCzM43kA5Ea8APeQLl26aNGiRfL391d4eLjKlnX9X0BwcLDL47S0NEVFRWn16tW5zlW5cuW7miEwMNDj30lLS5Mkffjhhy7RIN1ax+Mte/fu1YABAzRlyhT16NFDYWFhWrNmjebMmePxrEuWLMkVU35+fl6bFbiXES/APSQ4OFiRkZFuH9+iRQutXbtWVapUyXX14bbq1atr37596tixo6RbVxgSExPVokWLPI9v3LixHA6HduzYoW7duuXaf/vKT05OjnNbo0aNZLfbdfr06Xyv2DRs2NC5+Pi2r7766tdf5C/s2bNHEREReuWVV5zbTp06leu406dP68cff1R4eLjzecqUKaP69euratWqCg8P1/HjxzVgwACPnh+Ae1iwCyBfAwYMUKVKldSnTx/t2rVLJ06c0Pbt2zVy5EidPXtWkjRq1CjNnDlTGzdu1NGjRzVs2LACv6Oldu3aio6O1jPPPKONGzc6z7lu3TpJUkREhGw2m7Zs2aKff/5ZaWlpCg0N1ZgxYzR69GitWLFCx44d0zfffKM333zTuQj2+eef1z/+8Q+NHTtWycnJevfdd5WQkODR633ooYd0+vRprVmzRseOHVN8fHyei48DAgIUHR2tw4cPa9euXRo5cqT69++vatWqSZKmTJmiuLg4xcfH64cfftC3336r5cuX6/XXX/doHgB5I14A5CsoKEg7d+5UrVq11K9fPzVs2FDPPvusbt686bwS89JLL+npp59WdHS02rRpo9DQUD3++OMFnnfRokX6wx/+oGHDhqlBgwYaMmSI0tPTJUk1atTQlClTNH78eFWtWlXDhw+XJE2bNk0TJkxQXFycGjZsqJ49e+rDDz9UnTp1JN1ah/L+++9r48aNatq0qRYvXqwZM2Z49Hp79+6t0aNHa/jw4WrWrJn27NmjCRMm5DouMjJS/fr102OPPabu3burSZMmLh+FHjx4sJYuXarly5ercePG6tSpkxISEpyzAigcm5XfqjoAAIASiCsvAADAKMQLAAAwCvECAACMQrwAAACjEC8AAMAoxAsAADAK8QIAAIxCvAAAAKMQLwAAwCjECwAAMArxAgAAjPK/Bn1bLIjzhZoAAAAASUVORK5CYII=",
319
+ "text/plain": [
320
+ "<Figure size 640x480 with 1 Axes>"
321
+ ]
322
+ },
323
+ "metadata": {},
324
+ "output_type": "display_data"
325
+ }
326
+ ],
327
+ "source": [
328
+ "# Confusion matrix plotting\n",
329
+ "mtx = confusion_matrix(y_test, preds)\n",
330
+ "sns.heatmap(mtx, annot=True, fmt='d', linewidths=.5, cmap=\"Blues\", cbar=False)\n",
331
+ "plt.ylabel('True label')\n",
332
+ "plt.xlabel('Predicted label')\n",
333
+ "plt.show() # Display the plot"
334
+ ]
335
+ },
336
+ {
337
+ "cell_type": "code",
338
+ "execution_count": 14,
339
+ "metadata": {},
340
+ "outputs": [
341
+ {
342
+ "name": "stderr",
343
+ "output_type": "stream",
344
+ "text": [
345
+ "d:\\STUDY\\Sem3\\deeplearning\\DLENV\\lib\\site-packages\\keras\\src\\engine\\training.py:3079: UserWarning: You are saving your model as an HDF5 file via `model.save()`. This file format is considered legacy. We recommend using instead the native Keras format, e.g. `model.save('my_model.keras')`.\n",
346
+ " saving_api.save_model(\n"
347
+ ]
348
+ }
349
+ ],
350
+ "source": [
351
+ "# Save the trained model\n",
352
+ "model.save(\"dnn_smsspam_model.h5\")\n",
353
+ "dnn_smsspam_model = tf.keras.models.load_model('dnn_smsspam_model.h5')"
354
+ ]
355
+ },
356
+ {
357
+ "cell_type": "code",
358
+ "execution_count": 15,
359
+ "metadata": {},
360
+ "outputs": [],
361
+ "source": [
362
+ "def predict_message(input_text):\n",
363
+ " # Process input text similarly to training data\n",
364
+ " encoded_input = tokeniser.texts_to_sequences([input_text])\n",
365
+ " padded_input = tf.keras.preprocessing.sequence.pad_sequences(encoded_input, maxlen=max_length, padding='post')\n",
366
+ " \n",
367
+ " # Get the probabilities of being classified as \"Spam\" for each input\n",
368
+ " predictions = dnn_smsspam_model.predict(padded_input)\n",
369
+ " \n",
370
+ " # Define a threshold (e.g., 0.5) for classification\n",
371
+ " threshold = 0.5\n",
372
+ "\n",
373
+ " # Make the predictions based on the threshold for each input\n",
374
+ " results = []\n",
375
+ " for prediction in predictions:\n",
376
+ " if prediction > threshold:\n",
377
+ " results.append(\"Spam\")\n",
378
+ " else:\n",
379
+ " results.append(\"Not spam\")\n",
380
+ " \n",
381
+ " return results\n"
382
+ ]
383
+ },
384
+ {
385
+ "cell_type": "code",
386
+ "execution_count": 16,
387
+ "metadata": {},
388
+ "outputs": [
389
+ {
390
+ "name": "stdout",
391
+ "output_type": "stream",
392
+ "text": [
393
+ "1/1 [==============================] - 0s 57ms/step\n",
394
+ "Message: Your free ringtone is waiting to be collected. Simply text the password \"MIX\" to 85069 to verify. Get Usher and Britney. FML, PO Box 5249, MK17 92H. 450Ppw 16 haWatching telugu movie..wat abt u? \n",
395
+ "The message is classified as: ['Spam']\n"
396
+ ]
397
+ }
398
+ ],
399
+ "source": [
400
+ "# Take user input for prediction\n",
401
+ "user_input =('Your free ringtone is waiting to be collected. Simply text the password \"MIX\" to 85069 to verify. Get Usher and Britney. FML, PO Box 5249, MK17 92H. 450Ppw 16 haWatching telugu movie..wat abt u?')\n",
402
+ "prediction_result = predict_message(user_input)\n",
403
+ "print(f\"Message: {user_input} \\nThe message is classified as: {prediction_result}\")"
404
+ ]
405
+ },
406
+ {
407
+ "cell_type": "code",
408
+ "execution_count": 17,
409
+ "metadata": {},
410
+ "outputs": [
411
+ {
412
+ "name": "stdout",
413
+ "output_type": "stream",
414
+ "text": [
415
+ "1/1 [==============================] - 0s 23ms/step\n",
416
+ "Message: XXXMobileMovieClub: To use your credit, click the WAP link in the next txt message or click here>> http://wap. xxxmobilemovieclub.com?n=QJKGIGHJJGCBL \n",
417
+ "The message is classified as: ['Spam']\n"
418
+ ]
419
+ }
420
+ ],
421
+ "source": [
422
+ "\n",
423
+ "user_input_1 = ('XXXMobileMovieClub: To use your credit, click the WAP link in the next txt message or click here>> http://wap. xxxmobilemovieclub.com?n=QJKGIGHJJGCBL')\n",
424
+ "\n",
425
+ "\n",
426
+ "prediction_result_1 = predict_message(user_input_1)\n",
427
+ "print(f\"Message: {user_input_1} \\nThe message is classified as: {prediction_result_1}\")\n",
428
+ " "
429
+ ]
430
+ },
431
+ {
432
+ "cell_type": "code",
433
+ "execution_count": 18,
434
+ "metadata": {},
435
+ "outputs": [
436
+ {
437
+ "name": "stdout",
438
+ "output_type": "stream",
439
+ "text": [
440
+ "1/1 [==============================] - 0s 18ms/step\n",
441
+ "Message: Hi i want to speak to you \n",
442
+ "The message is classified as: ['Not spam']\n"
443
+ ]
444
+ }
445
+ ],
446
+ "source": [
447
+ "user_input= ('Hi i want to speak to you')\n",
448
+ "\n",
449
+ "\n",
450
+ "prediction_result= predict_message(user_input)\n",
451
+ "print(f\"Message: {user_input} \\nThe message is classified as: {prediction_result}\")"
452
+ ]
453
+ },
454
+ {
455
+ "cell_type": "code",
456
+ "execution_count": null,
457
+ "metadata": {},
458
+ "outputs": [],
459
+ "source": []
460
+ },
461
+ {
462
+ "cell_type": "code",
463
+ "execution_count": null,
464
+ "metadata": {},
465
+ "outputs": [],
466
+ "source": []
467
+ }
468
+ ],
469
+ "metadata": {
470
+ "kernelspec": {
471
+ "display_name": "DLENV",
472
+ "language": "python",
473
+ "name": "python3"
474
+ },
475
+ "language_info": {
476
+ "codemirror_mode": {
477
+ "name": "ipython",
478
+ "version": 3
479
+ },
480
+ "file_extension": ".py",
481
+ "mimetype": "text/x-python",
482
+ "name": "python",
483
+ "nbconvert_exporter": "python",
484
+ "pygments_lexer": "ipython3",
485
+ "version": "3.10.11"
486
+ }
487
+ },
488
+ "nbformat": 4,
489
+ "nbformat_minor": 2
490
+ }
dnn_smsspam_tokenizer.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9797f71f6e298d22ad16c8e17256351a5124192d536e452cf4192de8731c110f
3
+ size 290462