Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rasbt
GitHub Repository: rasbt/machine-learning-book
Path: blob/main/ch14/ch14_part1.py
1245 views
1
# coding: utf-8
2
3
4
import sys
5
from python_environment_check import check_packages
6
import torch
7
import numpy as np
8
import scipy.signal
9
from torchvision.io import read_image
10
import torch.nn as nn
11
import torchvision
12
from torchvision import transforms
13
from torch.utils.data import Subset
14
from torch.utils.data import DataLoader
15
import matplotlib.pyplot as plt
16
import os
17
18
# # Machine Learning with PyTorch and Scikit-Learn
19
# # -- Code Examples
20
21
# ## Package version checks
22
23
# Add folder to path in order to load from the check_packages.py script:
24
25
26
27
sys.path.insert(0, '..')
28
29
30
# Check recommended package versions:
31
32
33
34
35
36
d = {
37
'numpy': '1.21.2',
38
'scipy': '1.7.0',
39
'matplotlib': '3.4.3',
40
'torch': '1.8.0',
41
'torchvision': '0.9.0'
42
}
43
check_packages(d)
44
45
46
# # Chapter 14: Classifying Images with Deep Convolutional Neural Networks (Part 1/2)
47
48
# **Outline**
49
#
50
# - [The building blocks of CNNs](#The-building-blocks-of-CNNs)
51
# - [Understanding CNNs and feature hierarchies](#Understanding-CNNs-and-feature-hierarchies)
52
# - [Performing discrete convolutions](#Performing-discrete-convolutions)
53
# - [Discrete convolutions in one dimension](#Discrete-convolutions-in-one-dimension)
54
# - [Padding inputs to control the size of the output feature maps](#Padding-inputs-to-control-the-size-of-the-output-feature-maps)
55
# - [Determining the size of the convolution output](#Determining-the-size-of-the-convolution-output)
56
# - [Performing a discrete convolution in 2D](#Performing-a-discrete-convolution-in-2D)
57
# - [Subsampling layers](#Subsampling-layers)
58
# - [Putting everything together -- implementing a CNN](#Putting-everything-together----implementing-a-CNN)
59
# - [Working with multiple input or color channels](#Working-with-multiple-input-or-color-channels)
60
# - [Regularizing an NN with L2 regularization and dropout](#Regularizing-an-NN-with-L2-regularization-and-dropout)
61
# - [Loss functions for classification](#Loss-functions-for-classification)
62
# - [Implementing a deep CNN using PyTorch](#Implementing-a-deep-CNN-using-PyTorch)
63
# - [The multilayer CNN architecture](#The-multilayer-CNN-architecture)
64
# - [Loading and preprocessing the data](#Loading-and-preprocessing-the-data)
65
# - [Implementing a CNN using the torch.nn module](#Implementing-a-CNN-using-the-torch.nn-module)
66
# - [Configuring CNN layers in PyTorch](#Configuring-CNN-layers-in-PyTorch)
67
# - [Constructing a CNN in PyTorch](#Constructing-a-CNN-in-PyTorch)
68
69
70
71
72
73
# ## The building blocks of convolutional neural networks
74
#
75
# ### Understanding CNNs and feature hierarchies
76
77
78
79
80
81
#
82
#
83
# ### Performing discrete convolutions
84
#
85
# ### Discrete convolutions in one dimension
86
#
87
#
88
89
90
91
92
93
94
95
96
97
# ### Padding inputs to control the size of the output feature maps
98
#
99
#
100
101
102
103
104
105
# ### Determining the size of the convolution output
106
107
108
109
110
print('PyTorch version:', torch.__version__)
111
print('NumPy version: ', np.__version__)
112
113
114
115
116
def conv1d(x, w, p=0, s=1):
117
w_rot = np.array(w[::-1])
118
x_padded = np.array(x)
119
if p > 0:
120
zero_pad = np.zeros(shape=p)
121
x_padded = np.concatenate(
122
[zero_pad, x_padded, zero_pad])
123
res = []
124
for i in range(0, (int((len(x_padded) - len(w_rot))/s) + 1) * s, s):
125
res.append(np.sum(
126
x_padded[i:i + w_rot.shape[0]] * w_rot))
127
return np.array(res)
128
129
130
## Testing:
131
x = [1, 3, 2, 4, 5, 6, 1, 3]
132
w = [1, 0, 3, 1, 2]
133
134
print('Conv1d Implementation:',
135
conv1d(x, w, p=2, s=1))
136
137
print('Numpy Results:',
138
np.convolve(x, w, mode='same'))
139
140
141
# ### Performing a discrete convolution in 2D
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def conv2d(X, W, p=(0, 0), s=(1, 1)):
160
W_rot = np.array(W)[::-1,::-1]
161
X_orig = np.array(X)
162
n1 = X_orig.shape[0] + 2*p[0]
163
n2 = X_orig.shape[1] + 2*p[1]
164
X_padded = np.zeros(shape=(n1, n2))
165
X_padded[p[0]:p[0]+X_orig.shape[0],
166
p[1]:p[1]+X_orig.shape[1]] = X_orig
167
168
res = []
169
for i in range(0, (int((X_padded.shape[0] -
170
W_rot.shape[0]) / s[0]) + 1) * s[0], s[0]):
171
res.append([])
172
for j in range(0, (int((X_padded.shape[1] -
173
W_rot.shape[1]) / s[1]) + 1) * s[1], s[1]):
174
X_sub = X_padded[i:i + W_rot.shape[0],
175
j:j + W_rot.shape[1]]
176
res[-1].append(np.sum(X_sub * W_rot))
177
return(np.array(res))
178
179
X = [[1, 3, 2, 4], [5, 6, 1, 3], [1, 2, 0, 2], [3, 4, 3, 2]]
180
W = [[1, 0, 3], [1, 2, 1], [0, 1, 1]]
181
182
print('Conv2d Implementation:\n',
183
conv2d(X, W, p=(1, 1), s=(1, 1)))
184
185
186
print('SciPy Results:\n',
187
scipy.signal.convolve2d(X, W, mode='same'))
188
189
190
# ## Subsampling layers
191
192
193
194
195
196
# ## Putting everything together – implementing a CNN
197
#
198
# ### Working with multiple input or color channels
199
#
200
#
201
202
203
204
205
206
# **TIP: Reading an image file**
207
208
209
210
211
img = read_image('example-image.png')
212
213
print('Image shape:', img.shape)
214
print('Number of channels:', img.shape[0])
215
print('Image data type:', img.dtype)
216
print(img[:, 100:102, 100:102])
217
218
219
# ## Regularizing a neural network with L2 regularization and dropout
220
#
221
#
222
223
224
225
226
227
228
229
loss_func = nn.BCELoss()
230
loss = loss_func(torch.tensor([0.9]), torch.tensor([1.0]))
231
l2_lambda = 0.001
232
233
conv_layer = nn.Conv2d(in_channels=3, out_channels=5, kernel_size=5)
234
l2_penalty = l2_lambda * sum([(p**2).sum() for p in conv_layer.parameters()])
235
loss_with_penalty = loss + l2_penalty
236
237
linear_layer = nn.Linear(10, 16)
238
l2_penalty = l2_lambda * sum([(p**2).sum() for p in linear_layer.parameters()])
239
loss_with_penalty = loss + l2_penalty
240
241
242
# ## Loss Functions for Classification
243
#
244
# * **`nn.BCELoss()`**
245
# * `from_logits=False`
246
# * `from_logits=True`
247
#
248
# * **`nn.CrossEntropyLoss()`**
249
# * `from_logits=False`
250
# * `from_logits=True`
251
#
252
253
254
255
256
257
258
259
####### Binary Cross-entropy
260
logits = torch.tensor([0.8])
261
probas = torch.sigmoid(logits)
262
target = torch.tensor([1.0])
263
264
bce_loss_fn = nn.BCELoss()
265
bce_logits_loss_fn = nn.BCEWithLogitsLoss()
266
267
print(f'BCE (w Probas): {bce_loss_fn(probas, target):.4f}')
268
print(f'BCE (w Logits): {bce_logits_loss_fn(logits, target):.4f}')
269
270
271
####### Categorical Cross-entropy
272
logits = torch.tensor([[1.5, 0.8, 2.1]])
273
probas = torch.softmax(logits, dim=1)
274
target = torch.tensor([2])
275
276
cce_loss_fn = nn.NLLLoss()
277
cce_logits_loss_fn = nn.CrossEntropyLoss()
278
279
print(f'CCE (w Logits): {cce_logits_loss_fn(logits, target):.4f}')
280
print(f'CCE (w Probas): {cce_loss_fn(torch.log(probas), target):.4f}')
281
282
283
# ## Implementing a deep convolutional neural network using PyTorch
284
#
285
# ### The multilayer CNN architecture
286
287
288
289
290
291
# ### Loading and preprocessing the data
292
293
294
295
image_path = './'
296
transform = transforms.Compose([transforms.ToTensor()])
297
298
mnist_dataset = torchvision.datasets.MNIST(root=image_path,
299
train=True,
300
transform=transform,
301
download=True)
302
303
mnist_valid_dataset = Subset(mnist_dataset, torch.arange(10000))
304
mnist_train_dataset = Subset(mnist_dataset, torch.arange(10000, len(mnist_dataset)))
305
mnist_test_dataset = torchvision.datasets.MNIST(root=image_path,
306
train=False,
307
transform=transform,
308
download=False)
309
310
311
312
313
314
315
batch_size = 64
316
torch.manual_seed(1)
317
train_dl = DataLoader(mnist_train_dataset, batch_size, shuffle=True)
318
valid_dl = DataLoader(mnist_valid_dataset, batch_size, shuffle=False)
319
320
321
# ### Implementing a CNN using the torch.nn module
322
#
323
# #### Configuring CNN layers in PyTorch
324
#
325
# * **Conv2d:** `torch.nn.Conv2d`
326
# * `out_channels`
327
# * `kernel_size`
328
# * `stride`
329
# * `padding`
330
#
331
#
332
# * **MaxPool2d:** `torch.nn.MaxPool2d`
333
# * `kernel_size`
334
# * `stride`
335
# * `padding`
336
#
337
#
338
# * **Dropout** `torch.nn.Dropout`
339
# * `p`
340
341
# ### Constructing a CNN in PyTorch
342
343
344
345
model = nn.Sequential()
346
model.add_module('conv1', nn.Conv2d(in_channels=1, out_channels=32, kernel_size=5, padding=2))
347
model.add_module('relu1', nn.ReLU())
348
model.add_module('pool1', nn.MaxPool2d(kernel_size=2))
349
model.add_module('conv2', nn.Conv2d(in_channels=32, out_channels=64, kernel_size=5, padding=2))
350
model.add_module('relu2', nn.ReLU())
351
model.add_module('pool2', nn.MaxPool2d(kernel_size=2))
352
353
x = torch.ones((4, 1, 28, 28))
354
model(x).shape
355
356
357
358
359
model.add_module('flatten', nn.Flatten())
360
361
x = torch.ones((4, 1, 28, 28))
362
model(x).shape
363
364
365
366
367
model.add_module('fc1', nn.Linear(3136, 1024))
368
model.add_module('relu3', nn.ReLU())
369
model.add_module('dropout', nn.Dropout(p=0.5))
370
371
model.add_module('fc2', nn.Linear(1024, 10))
372
373
374
375
376
device = torch.device("cuda:0")
377
# device = torch.device("cpu")
378
379
model = model.to(device)
380
381
382
383
384
loss_fn = nn.CrossEntropyLoss()
385
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
386
387
def train(model, num_epochs, train_dl, valid_dl):
388
loss_hist_train = [0] * num_epochs
389
accuracy_hist_train = [0] * num_epochs
390
loss_hist_valid = [0] * num_epochs
391
accuracy_hist_valid = [0] * num_epochs
392
for epoch in range(num_epochs):
393
model.train()
394
for x_batch, y_batch in train_dl:
395
x_batch = x_batch.to(device)
396
y_batch = y_batch.to(device)
397
pred = model(x_batch)
398
loss = loss_fn(pred, y_batch)
399
loss.backward()
400
optimizer.step()
401
optimizer.zero_grad()
402
loss_hist_train[epoch] += loss.item()*y_batch.size(0)
403
is_correct = (torch.argmax(pred, dim=1) == y_batch).float()
404
accuracy_hist_train[epoch] += is_correct.sum().cpu()
405
406
loss_hist_train[epoch] /= len(train_dl.dataset)
407
accuracy_hist_train[epoch] /= len(train_dl.dataset)
408
409
model.eval()
410
with torch.no_grad():
411
for x_batch, y_batch in valid_dl:
412
x_batch = x_batch.to(device)
413
y_batch = y_batch.to(device)
414
pred = model(x_batch)
415
loss = loss_fn(pred, y_batch)
416
loss_hist_valid[epoch] += loss.item()*y_batch.size(0)
417
is_correct = (torch.argmax(pred, dim=1) == y_batch).float()
418
accuracy_hist_valid[epoch] += is_correct.sum().cpu()
419
420
loss_hist_valid[epoch] /= len(valid_dl.dataset)
421
accuracy_hist_valid[epoch] /= len(valid_dl.dataset)
422
423
print(f'Epoch {epoch+1} accuracy: {accuracy_hist_train[epoch]:.4f} val_accuracy: {accuracy_hist_valid[epoch]:.4f}')
424
return loss_hist_train, loss_hist_valid, accuracy_hist_train, accuracy_hist_valid
425
426
torch.manual_seed(1)
427
num_epochs = 20
428
hist = train(model, num_epochs, train_dl, valid_dl)
429
430
431
432
433
434
435
x_arr = np.arange(len(hist[0])) + 1
436
437
fig = plt.figure(figsize=(12, 4))
438
ax = fig.add_subplot(1, 2, 1)
439
ax.plot(x_arr, hist[0], '-o', label='Train loss')
440
ax.plot(x_arr, hist[1], '--<', label='Validation loss')
441
ax.set_xlabel('Epoch', size=15)
442
ax.set_ylabel('Loss', size=15)
443
ax.legend(fontsize=15)
444
ax = fig.add_subplot(1, 2, 2)
445
ax.plot(x_arr, hist[2], '-o', label='Train acc.')
446
ax.plot(x_arr, hist[3], '--<', label='Validation acc.')
447
ax.legend(fontsize=15)
448
ax.set_xlabel('Epoch', size=15)
449
ax.set_ylabel('Accuracy', size=15)
450
451
#plt.savefig('figures/14_13.png')
452
plt.show()
453
454
455
456
457
torch.cuda.synchronize()
458
model_cpu = model.cpu()
459
pred = model(mnist_test_dataset.data.unsqueeze(1) / 255.)
460
is_correct = (torch.argmax(pred, dim=1) == mnist_test_dataset.targets).float()
461
print(f'Test accuracy: {is_correct.mean():.4f}')
462
463
464
465
466
fig = plt.figure(figsize=(12, 4))
467
for i in range(12):
468
ax = fig.add_subplot(2, 6, i+1)
469
ax.set_xticks([]); ax.set_yticks([])
470
img = mnist_test_dataset[i][0][0, :, :]
471
pred = model(img.unsqueeze(0).unsqueeze(1))
472
y_pred = torch.argmax(pred)
473
ax.imshow(img, cmap='gray_r')
474
ax.text(0.9, 0.1, y_pred.item(),
475
size=15, color='blue',
476
horizontalalignment='center',
477
verticalalignment='center',
478
transform=ax.transAxes)
479
480
481
plt.savefig('figures/14_14.png')
482
plt.show()
483
484
485
486
487
488
if not os.path.exists('models'):
489
os.mkdir('models')
490
491
path = 'models/mnist-cnn.ph'
492
torch.save(model, path)
493
494
495
496
# ----
497
#
498
# Readers may ignore the next cell.
499
500
501
502
503
504
505
506
507
508
509