7.md 78.6 KB
Newer Older
W
wizardforcel 已提交
1
# 七、附录
W
wizardforcel 已提交
2 3 4 5 6 7 8

# 1.深度学习和 PyTorch 简介

## 活动 1.01:创建单层神经网络

### 解决方案

W
wizardforcel 已提交
9
1.  导入所需的库,包括 pandas,用于导入 CSV 文件。
W
wizardforcel 已提交
10

W
wizardforcel 已提交
11 12 13 14 15 16
    ```py
    import pandas as pd
    import torch
    import torch.nn as nn
    import matplotlib.pyplot as plt
    ```
W
wizardforcel 已提交
17

W
wizardforcel 已提交
18
2.  读取包含数据集的 CSV 文件。
W
wizardforcel 已提交
19

W
wizardforcel 已提交
20 21 22
    ```py
    data = pd.read_csv("SomervilleHappinessSurvey2015.csv")
    ```
W
wizardforcel 已提交
23

W
wizardforcel 已提交
24
3.  将输入特征与目标分开。注意,目标位于 CSV 文件的第一列。将值转换为张量,确保值转换为浮点数。
W
wizardforcel 已提交
25

W
wizardforcel 已提交
26 27 28 29
    ```py
    x = torch.tensor(data.iloc[:,1:].values).float()
    y = torch.tensor(data.iloc[:,:1].values).float()
    ```
W
wizardforcel 已提交
30

W
wizardforcel 已提交
31
4.  定义模型的架构,并将其存储在一个名为`model`的变量中。记住要创建一个单层模型。
W
wizardforcel 已提交
32

W
wizardforcel 已提交
33 34 35 36
    ```py
    model = nn.Sequential(nn.Linear(6, 1),
                          nn.Sigmoid())
    ```
W
wizardforcel 已提交
37

W
wizardforcel 已提交
38
5.  定义要使用的损失函数。使用 MSE 损失函数。
W
wizardforcel 已提交
39

W
wizardforcel 已提交
40 41 42
    ```py
    loss_function = torch.nn.MSELoss()
    ```
W
wizardforcel 已提交
43

W
wizardforcel 已提交
44
6.  定义你模型的优化器。使用亚当优化器和学习率`0.01`
W
wizardforcel 已提交
45

W
wizardforcel 已提交
46 47 48
    ```py
    optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
    ```
W
wizardforcel 已提交
49

W
wizardforcel 已提交
50
7.  运行优化 100 次迭代。每迭代 10 次,打印并保存损失值。
W
wizardforcel 已提交
51

W
wizardforcel 已提交
52 53 54 55 56 57 58 59 60 61 62 63
    ```py
    losses = []
    for i in range(100):
        y_pred = model(x)
        loss = loss_function(y_pred, y)
        losses.append(loss.item())
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if i%10 == 0:
            print(loss.item())
    ```
W
wizardforcel 已提交
64

W
wizardforcel 已提交
65
    最终损失应约为`0.24`。
W
wizardforcel 已提交
66

W
wizardforcel 已提交
67
8.  做一个线图来显示每个迭代步骤的损失值。
W
wizardforcel 已提交
68

W
wizardforcel 已提交
69 70
    ```py
    plt.plot(range(0,100), losses)
W
wizardforcel 已提交
71
    plt.show()
W
wizardforcel 已提交
72
    ```
W
wizardforcel 已提交
73 74 75 76 77

    结果图应如下所示:

![Figure 1.4: Loss function throughout the training process ](img/B15778_01_04.jpg)

W
wizardforcel 已提交
78
图 1.4:整个训练过程中的损失函数
W
wizardforcel 已提交
79

W
wizardforcel 已提交
80
这意味着训练过程能够使损失函数最小化,这意味着结果模型将可能能够绘制出市民对城市服务的满意度与他们对行政管理是否满意之间的关系。
W
wizardforcel 已提交
81 82 83

注意

W
wizardforcel 已提交
84
要访问此特定部分的源代码,请参考[这里](https://packt.live/2ZufWiI)
W
wizardforcel 已提交
85

W
wizardforcel 已提交
86
您也可以通过[这里](https://packt.live/2BZhyZF)在线运行此示例。 您必须执行整个笔记本才能获得所需的结果。
W
wizardforcel 已提交
87

W
wizardforcel 已提交
88
# 2.神经网络的构建块
W
wizardforcel 已提交
89 90 91 92 93

## 活动 2.01:执行数据准备

### 解决方案

W
wizardforcel 已提交
94
1.  导入所需的库。
W
wizardforcel 已提交
95

W
wizardforcel 已提交
96 97 98
    ```py
    import pandas as pd
    ```
W
wizardforcel 已提交
99

W
wizardforcel 已提交
100
2.  使用 pandas,加载`.csv`文件。
W
wizardforcel 已提交
101

W
wizardforcel 已提交
102 103 104 105
    ```py
    data = pd.read_csv("YearPredictionMSD.csv", nrows=50000)
    data.head()
    ```
W
wizardforcel 已提交
106 107 108

    注意

W
wizardforcel 已提交
109
    为避免内存限制,在读取文本文件时,请使用`nrows`自变量,以读取整个数据集的较小部分。 在前面的示例中,我们正在读取前 50,000 行。
W
wizardforcel 已提交
110 111 112 113 114

    输出如下:

    ![Figure 2.33: YearPredictionMSD.csv ](img/B15778_02_33.jpg)

W
wizardforcel 已提交
115
    图 2.33:`YearPredictionMSD.csv`
W
wizardforcel 已提交
116

W
wizardforcel 已提交
117
3.  核实数据集中是否存在任何定性数据。
W
wizardforcel 已提交
118

W
wizardforcel 已提交
119
    ```py
W
wizardforcel 已提交
120
    cols = data.columns
W
wizardforcel 已提交
121 122 123
    num_cols = data._get_numeric_data().columns
    list(set(cols) - set(num_cols))
    ```
W
wizardforcel 已提交
124 125 126

    输出应为空列表,这意味着没有定性特征。

W
wizardforcel 已提交
127
4.  检查是否有缺失值。
W
wizardforcel 已提交
128

W
wizardforcel 已提交
129
    如果在先前用于此目的的代码行中添加一个附加的`sum()`函数,则将获得整个数据集中的缺失值之和,而无需按列进行区分:
W
wizardforcel 已提交
130

W
wizardforcel 已提交
131 132 133
    ```py
    data.isnull().sum().sum()
    ```
W
wizardforcel 已提交
134

W
wizardforcel 已提交
135
    输出应为`0`,这意味着所有特征均不包含缺失值。
W
wizardforcel 已提交
136

W
wizardforcel 已提交
137
5.  检查是否有异常值。
W
wizardforcel 已提交
138

W
wizardforcel 已提交
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
    ```py
    outliers = {}
    for i in range(data.shape[1]):
        min_t = data[data.columns[i]].mean() \
                - (3 * data[data.columns[i]].std())
        max_t = data[data.columns[i]].mean() \
                + (3 * data[data.columns[i]].std())
        count = 0
        for j in data[data.columns[i]]:
            if j < min_t or j > max_t:
                count += 1
        percentage = count/data.shape[0]
        outliers[data.columns[i]] = "%.3f" % percentage
    print(outliers)
    ```
W
wizardforcel 已提交
154

W
wizardforcel 已提交
155
    输出字典应显示所有特征均不包含代表超过 5% 数据的离群值。
W
wizardforcel 已提交
156

W
wizardforcel 已提交
157
6.  将特征从目标数据中分离出来。
W
wizardforcel 已提交
158

W
wizardforcel 已提交
159 160 161 162
    ```py
    X = data.iloc[:, 1:]
    Y = data.iloc[:, 0]
    ```
W
wizardforcel 已提交
163

W
wizardforcel 已提交
164
7.  使用标准化方法对特征数据进行重新缩放。
W
wizardforcel 已提交
165

W
wizardforcel 已提交
166 167 168 169
    ```py
    X = (X - X.mean())/X.std()
    X.head()
    ```
W
wizardforcel 已提交
170 171 172 173 174

    输出如下:

    ![Figure 2.34: Rescaled features data ](img/B15778_02_34.jpg)

W
wizardforcel 已提交
175
    图 2.34:重新缩放的特征数据
W
wizardforcel 已提交
176

W
wizardforcel 已提交
177
8.  将数据分成三组:训练、验证和测试。使用你喜欢的方法。
W
wizardforcel 已提交
178

W
wizardforcel 已提交
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    ```py
    from sklearn.model_selection import train_test_split
    X_shuffle = X.sample(frac=1, random_state=0)
    Y_shuffle = Y.sample(frac=1, random_state=0)
    x_new, x_test, \
    y_new, y_test = train_test_split(X_shuffle, \
                                     Y_shuffle, \
                                     test_size=0.2, \
                                     random_state=0)
    dev_per = x_test.shape[0]/x_new.shape[0]
    x_train, x_dev, \
    y_train, y_dev = train_test_split(x_new, \
                                      y_new, \
                                      test_size=dev_per, \
                                      random_state=0)
    ```
W
wizardforcel 已提交
195

W
wizardforcel 已提交
196
9.  打印所得形状如下。
W
wizardforcel 已提交
197

W
wizardforcel 已提交
198 199 200 201 202
    ```py
    print(x_train.shape, y_train.shape)
    print(x_dev.shape, y_dev.shape)
    print(x_test.shape, y_test.shape)
    ```
W
wizardforcel 已提交
203 204 205

    输出应如下所示:

W
wizardforcel 已提交
206
    ```py
W
wizardforcel 已提交
207 208 209
    (30000, 90) (30000, )
    (10000, 90) (10000, )
    (10000, 90) (10000, )
W
wizardforcel 已提交
210
    ```
W
wizardforcel 已提交
211 212 213

    注意

W
wizardforcel 已提交
214
    要访问此特定部分的源代码,请参考[这里](https://packt.live/31ukVTj)。
W
wizardforcel 已提交
215

W
wizardforcel 已提交
216
    您也可以通过[这里](https://packt.live/3dLWMdd)在线运行此示例。 您必须执行整个笔记本才能获得所需的结果。
W
wizardforcel 已提交
217 218 219 220 221

## 活动 2.02:为回归问题开发深度学习解决方案

### 解决方案

W
wizardforcel 已提交
222
1.  导入所需的库。
W
wizardforcel 已提交
223

W
wizardforcel 已提交
224 225 226 227
    ```py
    import torch
    import torch.nn as nn
    ```
W
wizardforcel 已提交
228

W
wizardforcel 已提交
229
2.  将我们在上一个活动中创建的所有三组数据的特征从目标中分割出来。将`DataFrame`转换为张量。
W
wizardforcel 已提交
230

W
wizardforcel 已提交
231 232 233 234 235 236 237 238
    ```py
    x_train = torch.tensor(x_train.values).float()
    y_train = torch.tensor(y_train.values).float()
    x_dev = torch.tensor(x_dev.values).float()
    y_dev = torch.tensor(y_dev.values).float()
    x_test = torch.tensor(x_test.values).float()
    y_test = torch.tensor(y_test.values).float()
    ```
W
wizardforcel 已提交
239

W
wizardforcel 已提交
240
3.  定义网络的架构。可以自由尝试不同的层数和每层单元数的组合。
W
wizardforcel 已提交
241

W
wizardforcel 已提交
242 243 244 245 246 247 248 249 250
    ```py
    model = nn.Sequential(nn.Linear(x_train.shape[1], 10), \
                          nn.ReLU(), \
                          nn.Linear(10, 7), \
                          nn.ReLU(), \
                          nn.Linear(7, 5), \
                          nn.ReLU(), \
                          nn.Linear(5, 1))
    ```
W
wizardforcel 已提交
251

W
wizardforcel 已提交
252
4.  定义损失函数和优化器算法。
W
wizardforcel 已提交
253

W
wizardforcel 已提交
254 255 256 257
    ```py
    loss_function = torch.nn.MSELoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
    ```
W
wizardforcel 已提交
258

W
wizardforcel 已提交
259
5.  使用`for`循环来训练网络,迭代步数为 3000 步。
W
wizardforcel 已提交
260

W
wizardforcel 已提交
261 262 263 264 265 266 267 268 269 270
    ```py
    for i in range(3000):
        y_pred = model(x_train).squeeze()
        loss = loss_function(y_pred, y_train)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if i%250 == 0:
            print(i, loss.item())
    ```
W
wizardforcel 已提交
271

W
wizardforcel 已提交
272
6.  通过对测试集的第一个实例进行预测,并与真实情况进行比较来测试你的模型。
W
wizardforcel 已提交
273

W
wizardforcel 已提交
274 275 276 277 278
    ```py
    pred = model(x_test[0])
    print("Ground truth:", y_test[0].item(), \
          "Prediction:", pred.item())
    ```
W
wizardforcel 已提交
279 280 281

    您的输出应类似于以下内容:

W
wizardforcel 已提交
282 283 284
    ```py
    Ground truth: 1995.0 Prediction: 1998.0279541015625
    ```
W
wizardforcel 已提交
285 286 287

    注意

W
wizardforcel 已提交
288
    要访问此特定部分的源代码,请参考[这里](https://packt.live/2CUDSnP)。
W
wizardforcel 已提交
289

W
wizardforcel 已提交
290
    您也可以通过[这里](https://packt.live/3eQ1yI2)在线运行此示例。 您必须执行整个笔记本才能获得所需的结果。
W
wizardforcel 已提交
291 292 293 294 295 296 297

# 3.使用 DNN 的分类问题

## 活动 3.01:构建人工神经网络

解:

W
wizardforcel 已提交
298
1.  导入以下库:
W
wizardforcel 已提交
299

W
wizardforcel 已提交
300 301 302 303 304 305 306 307 308 309 310 311
    ```py
    import pandas as pd
    import numpy as np
    from sklearn.model_selection import train_test_split
    from sklearn.utils import shuffle
    from sklearn.metrics import accuracy_score
    import torch
    from torch import nn, optim
    import torch.nn.functional as F
    import matplotlib.pyplot as plt
    torch.manual_seed(0)
    ```
W
wizardforcel 已提交
312

W
wizardforcel 已提交
313
2.  读取之前准备好的数据集,该数据集应该命名为`dccc_prepared.csv`
W
wizardforcel 已提交
314

W
wizardforcel 已提交
315 316 317 318
    ```py
    data = pd.read_csv("dccc_prepared.csv")
    data.head()
    ```
W
wizardforcel 已提交
319 320 321 322 323

    输出应如下所示:

    ![Figure 3.14: dccc_prepared.csv ](img/B15778_03_14.jpg)

W
wizardforcel 已提交
324
    图 3.14:`dccc_prepared.csv`
W
wizardforcel 已提交
325

W
wizardforcel 已提交
326
3.  将特征与目标分开。
W
wizardforcel 已提交
327

W
wizardforcel 已提交
328 329 330 331
    ```py
    X = data.iloc[:,:-1]
    y = data["default payment next month"]
    ```
W
wizardforcel 已提交
332

W
wizardforcel 已提交
333
4.  使用 scikit-learn 的`train_test_split`函数,将数据集分割成训练集、验证集和测试集。使用 60:20:20 的分割比例。将`random_state`设置为 0。
W
wizardforcel 已提交
334

W
wizardforcel 已提交
335 336 337 338 339 340 341 342 343 344
    ```py
    X_new, X_test, \
    y_new, y_test = train_test_split(X, y, test_size=0.2, \
                                     random_state=0)
    dev_per = X_test.shape[0]/X_new.shape[0]
    X_train, X_dev, \
    y_train, y_dev = train_test_split(X_new, y_new, \
                                      test_size=dev_per, \
                                      random_state=0)
    ```
W
wizardforcel 已提交
345 346 347

    您可以使用以下代码打印每个集合的最终形状:

W
wizardforcel 已提交
348 349 350 351 352
    ```py
    print("Training sets:",X_train.shape, y_train.shape)
    print("Validation sets:",X_dev.shape, y_dev.shape)
    print("Testing sets:",X_test.shape, y_test.shape)
    ```
W
wizardforcel 已提交
353 354 355

    每个集合的最终形状如下所示:

W
wizardforcel 已提交
356 357 358 359 360
    ```py
    Training sets: (28036, 22) (28036,)
    Validation sets: (9346, 22) (9346,)
    Testing sets: (9346, 22) (9346,)
    ```
W
wizardforcel 已提交
361

W
wizardforcel 已提交
362
5.  将验证集和测试集转换为张量,记住特征矩阵应该是`float`类型,而目标矩阵不应该。训练集暂不转换,因为它们将进行进一步的转换。
W
wizardforcel 已提交
363

W
wizardforcel 已提交
364 365 366 367 368 369
    ```py
    X_dev_torch = torch.tensor(X_dev.values).float()
    y_dev_torch = torch.tensor(y_dev.values)
    X_test_torch = torch.tensor(X_test.values).float()
    y_test_torch = torch.tensor(y_test.values)
    ```
W
wizardforcel 已提交
370

W
wizardforcel 已提交
371
6.  构建一个自定义模块类,用于定义网络的层。包括一个前向函数,指定将应用于每层输出的激活函数。对所有层都使用 **ReLU**,除了输出,你应该使用`log_softmax`
W
wizardforcel 已提交
372

W
wizardforcel 已提交
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
    ```py
    class Classifier(nn.Module):
        def __init__(self, input_size):
            super().__init__()
            self.hidden_1 = nn.Linear(input_size, 10)
            self.hidden_2 = nn.Linear(10, 10)
            self.hidden_3 = nn.Linear(10, 10)
            self.output = nn.Linear(10, 2)
        def forward(self, x):
            z = F.relu(self.hidden_1(x))
            z = F.relu(self.hidden_2(z))
            z = F.relu(self.hidden_3(z))
            out = F.log_softmax(self.output(z), dim=1)
            return out
    ```
W
wizardforcel 已提交
388

W
wizardforcel 已提交
389
7.  实例化模型并定义训练模型所需的所有变量。设置周期数为`50`,批次大小为`128`。使用`0.001`的学习率。
W
wizardforcel 已提交
390

W
wizardforcel 已提交
391 392 393 394 395
    ```py
    model = Classifier(X_train.shape[1])
    criterion = nn.NLLLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    epochs = 50
W
wizardforcel 已提交
396
    batch_size = 128
W
wizardforcel 已提交
397
    ```
W
wizardforcel 已提交
398

W
wizardforcel 已提交
399
8.  使用训练集的数据来训练网络。使用验证集来衡量表现。要做到这一点,请保存每个周期的训练集和验证集的损失和准确率。
W
wizardforcel 已提交
400

W
wizardforcel 已提交
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
    ```py
    train_losses, dev_losses, \
    train_acc, dev_acc = [], [], [], []
    for e in range(epochs):
        X_, y_ = shuffle(X_train, y_train)
        running_loss = 0
        running_acc = 0
        iterations = 0
        for i in range(0, len(X_), batch_size):
            iterations += 1
            b = i + batch_size
            X_batch = torch.tensor(X_.iloc[i:b,:].values).float()
            y_batch = torch.tensor(y_.iloc[i:b].values)
            pred = model(X_batch)
            loss = criterion(pred, y_batch)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            running_loss += loss.item()
            ps = torch.exp(pred)
            top_p, top_class = ps.topk(1, dim=1)
            running_acc += accuracy_score(y_batch, top_class)
        dev_loss = 0
        acc = 0
        with torch.no_grad():
            pred_dev = model(X_dev_torch)
            dev_loss = criterion(pred_dev, y_dev_torch)
            ps_dev = torch.exp(pred_dev)
            top_p, top_class_dev = ps_dev.topk(1, dim=1)
            acc = accuracy_score(y_dev_torch, top_class_dev)
        train_losses.append(running_loss/iterations)
        dev_losses.append(dev_loss)
        train_acc.append(running_acc/iterations)
        dev_acc.append(acc)
        print("Epoch: {}/{}.. ".format(e+1, epochs),\
              "Training Loss: {:.3f}.. "\
              .format(running_loss/iterations),\
              "Validation Loss: {:.3f}.. ".format(dev_loss), \
              "Training Accuracy: {:.3f}.. "\
              .format(running_acc/iterations), \
              "Validation Accuracy: {:.3f}".format(acc))
    ```
W
wizardforcel 已提交
443

W
wizardforcel 已提交
444
9.  绘出两组的损失。
W
wizardforcel 已提交
445

W
wizardforcel 已提交
446 447 448 449 450
    ```py
    fig = plt.figure(figsize=(15, 5))
    plt.plot(train_losses, label='Training loss')
    plt.plot(dev_losses, label='Validation loss')
    plt.legend(frameon=False, fontsize=15)
W
wizardforcel 已提交
451
    plt.show()
W
wizardforcel 已提交
452
    ```
W
wizardforcel 已提交
453

W
wizardforcel 已提交
454
    考虑到打乱训练数据可能会得出略有不同的结果,结果图应与此处显示的图相似,尽管有所不同。
W
wizardforcel 已提交
455 456 457 458 459

    ![Figure 3.15: A plot displaying the training and validation losses ](img/B15778_03_15.jpg)

    图 3.15:显示训练和验证损失的图

W
wizardforcel 已提交
460
0.  绘制两组的精度。
W
wizardforcel 已提交
461

W
wizardforcel 已提交
462 463 464 465 466
    ```py
    fig = plt.figure(figsize=(15, 5))
    plt.plot(train_acc, label="Training accuracy")
    plt.plot(dev_acc, label="Validation accuracy")
    plt.legend(frameon=False, fontsize=15)
W
wizardforcel 已提交
467
    plt.show()
W
wizardforcel 已提交
468
    ```
W
wizardforcel 已提交
469 470 471 472 473 474 475 476 477

    这是从此代码段派生的图:

![Figure 3.16: A plot displaying the accuracy of the sets ](img/B15778_03_16.jpg)

图 3.16:显示集合精度的图

注意

W
wizardforcel 已提交
478
要访问此特定部分的源代码,请参考[这里](https://packt.live/2Vz6BoK)
W
wizardforcel 已提交
479

W
wizardforcel 已提交
480
您也可以通过[这里](https://packt.live/2NNBuRS)在线运行此示例。 您必须执行整个笔记本才能获得所需的结果。
W
wizardforcel 已提交
481

W
wizardforcel 已提交
482
## 练习 3.02:提高模型的表现
W
wizardforcel 已提交
483 484 485

解:

W
wizardforcel 已提交
486
1.  导入你在上一个活动中使用的相同的库。
W
wizardforcel 已提交
487

W
wizardforcel 已提交
488 489 490 491 492 493 494 495 496 497 498 499
    ```py
    import pandas as pd
    import numpy as np
    from sklearn.model_selection import train_test_split
    from sklearn.utils import shuffle
    from sklearn.metrics import accuracy_score
    import torch
    from torch import nn, optim
    import torch.nn.functional as F
    import matplotlib.pyplot as plt
    torch.manual_seed(0)```
    ```
W
wizardforcel 已提交
500

W
wizardforcel 已提交
501
2.  加载数据并从目标中拆分特征。接下来,使用 60:20:20 的分割比例将数据分割成三个子集(训练、验证和测试)。最后,将验证和测试集转换为 PyTorch 张量,就像您在上一个活动中所做的那样。
W
wizardforcel 已提交
502

W
wizardforcel 已提交
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519
    ```py
    data = pd.read_csv("dccc_prepared.csv")
    X = data.iloc[:,:-1]
    y = data["default payment next month"]
    X_new, X_test, \
    y_new, y_test = train_test_split(X, y, test_size=0.2, \
                                     random_state=0)
    dev_per = X_test.shape[0]/X_new.shape[0]
    X_train, X_dev, \
    y_train, y_dev = train_test_split(X_new, y_new, \
                                      test_size=dev_per, \
                                      random_state=0)
    X_dev_torch = torch.tensor(X_dev.values).float()
    y_dev_torch = torch.tensor(y_dev.values)
    X_test_torch = torch.tensor(X_test.values).float()
    y_test_torch = torch.tensor(y_test.values)
    ```
W
wizardforcel 已提交
520

W
wizardforcel 已提交
521
3.  考虑到该模型存在较高的偏差,重点应放在增加周期的数量上,或通过在每层中增加额外的层或单元来增加网络的规模。目标应该是将验证集的准确率近似到 80%。
W
wizardforcel 已提交
522

W
wizardforcel 已提交
523
    之后,将显示表现最佳的模型,该模型是在几次微调尝试之后实现的。 首先,定义模型架构和正向传播,如以下代码片段所示:
W
wizardforcel 已提交
524

W
wizardforcel 已提交
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
    ```py
    class Classifier(nn.Module):
        def __init__(self, input_size):
            super().__init__()
            self.hidden_1 = nn.Linear(input_size, 100)
            self.hidden_2 = nn.Linear(100, 100)
            self.hidden_3 = nn.Linear(100, 50)
            self.hidden_4 = nn.Linear(50,50)
            self.output = nn.Linear(50, 2)
            self.dropout = nn.Dropout(p=0.1)
        def forward(self, x):
            z = self.dropout(F.relu(self.hidden_1(x)))
            z = self.dropout(F.relu(self.hidden_2(z)))
            z = self.dropout(F.relu(self.hidden_3(z)))
            z = self.dropout(F.relu(self.hidden_4(z)))
            out = F.log_softmax(self.output(z), dim=1)
            return out
    ```
W
wizardforcel 已提交
543

W
wizardforcel 已提交
544
    接下来,定义训练过程的不同参数。 这包括损失函数,优化算法,批量大小和周期数,如以下代码所示:
W
wizardforcel 已提交
545

W
wizardforcel 已提交
546 547 548 549 550
    ```py
    model = Classifier(X_train.shape[1])
    criterion = nn.NLLLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    epochs = 4000
W
wizardforcel 已提交
551
    batch_size = 128
W
wizardforcel 已提交
552
    ```
W
wizardforcel 已提交
553

W
wizardforcel 已提交
554
    最后,按照以下代码片段处理训练过程:
W
wizardforcel 已提交
555

W
wizardforcel 已提交
556 557
    ```py
    train_losses, dev_losses, train_acc, dev_acc= [], [], [], []
W
wizardforcel 已提交
558
    x_axis = []
W
wizardforcel 已提交
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601
    for e in range(1, epochs + 1):
        X_, y_ = shuffle(X_train, y_train)
        running_loss = 0
        running_acc = 0
        iterations = 0
        for i in range(0, len(X_), batch_size):
            iterations += 1
            b = i + batch_size
            X_batch = torch.tensor(X_.iloc[i:b,:].values).float()
            y_batch = torch.tensor(y_.iloc[i:b].values)
            log_ps = model(X_batch)
            loss = criterion(log_ps, y_batch)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            running_loss += loss.item()
            ps = torch.exp(log_ps)
            top_p, top_class = ps.topk(1, dim=1)
            running_acc += accuracy_score(y_batch, top_class)
        dev_loss = 0
        acc = 0
        with torch.no_grad():
            model.eval()
            log_dev = model(X_dev_torch)
            dev_loss = criterion(log_dev, y_dev_torch)
            ps_dev = torch.exp(log_dev)
            top_p, top_class_dev = ps_dev.topk(1, dim=1)
            acc = accuracy_score(y_dev_torch, top_class_dev)
        model.train()
        if e%50 == 0 or e == 1:
            x_axis.append(e)
            train_losses.append(running_loss/iterations)
            dev_losses.append(dev_loss)
            train_acc.append(running_acc/iterations)
            dev_acc.append(acc)
            print("Epoch: {}/{}.. ".format(e, epochs), \
                  "Training Loss: {:.3f}.. "\
                  .format(running_loss/iterations), \
                  "Validation Loss: {:.3f}.. ".format(dev_loss),\
                  "Training Accuracy: {:.3f}.. "\
                  .format(running_acc/iterations), \
                  "Validation Accuracy: {:.3f}".format(acc))
    ```
W
wizardforcel 已提交
602 603 604

    注意

W
wizardforcel 已提交
605
    可以在以前共享的 GitHub 存储库中找到此活动随附的 Jupyter 笔记本。 在那里,您会发现对模型进行微调的各种尝试及其结果。 表现最佳的模型可以在笔记本电脑的末尾找到。
W
wizardforcel 已提交
606

W
wizardforcel 已提交
607
4.  绘制两组数据的损失和准确率。
W
wizardforcel 已提交
608 609 610

    注意

W
wizardforcel 已提交
611
    请记住,此处显示的结果与您的结果不完全匹配。 这主要是由于训练网络时使用了打乱函数。
W
wizardforcel 已提交
612 613 614

    使用以下代码绘制损失:

W
wizardforcel 已提交
615 616 617 618 619
    ```py
    fig = plt.figure(figsize=(15, 5))
    plt.plot(x_axis,train_losses, label='Training loss')
    plt.plot(x_axis, dev_losses, label='Validation loss')
    plt.legend(frameon=False , fontsize=15)
W
wizardforcel 已提交
620
    plt.show()
W
wizardforcel 已提交
621
    ```
W
wizardforcel 已提交
622 623 624 625 626 627 628 629 630

    运行前面的代码将显示以下图:

    ![Figure 3.17: A plot displaying the loss of the sets ](img/B15778_03_17.jpg)

    图 3.17:显示集合损失的图

    使用以下代码来绘制精度:

W
wizardforcel 已提交
631 632 633 634 635
    ```py
    fig = plt.figure(figsize=(15, 5))
    plt.plot(x_axis, train_acc, label="Training accuracy")
    plt.plot(x_axis, dev_acc, label="Validation accuracy")
    plt.legend(frameon=False , fontsize=15)
W
wizardforcel 已提交
636
    plt.show()
W
wizardforcel 已提交
637
    ```
W
wizardforcel 已提交
638 639 640 641 642 643 644

    运行前面的代码将显示以下图:

    ![Figure 3.18: A plot displaying the accuracy of the sets ](img/B15778_03_18.jpg)

    图 3.18:显示集合精度的图

W
wizardforcel 已提交
645
5.  使用表现最好的模型,对测试集(在微调过程中不应该使用)进行预测。通过计算模型在该集上的准确率,将预测结果与基本事实进行比较。
W
wizardforcel 已提交
646

W
wizardforcel 已提交
647 648 649 650 651 652 653 654
    ```py
    model.eval()
    test_pred = model(X_test_torch)
    test_pred = torch.exp(test_pred)
    top_p, top_class_test = test_pred.topk(1, dim=1)
    acc_test = accuracy_score(y_test_torch, top_class_test)
    print(acc_test)
    ```
W
wizardforcel 已提交
655

W
wizardforcel 已提交
656
    通过模型架构和此处定义的参数获得的精度应为 80% 左右。
W
wizardforcel 已提交
657 658 659

    注意

W
wizardforcel 已提交
660
    要访问此特定部分的源代码,请参考[这里](https://packt.live/2Bs42hh)。
W
wizardforcel 已提交
661 662 663 664 665 666 667

    本部分当前没有在线交互示例,需要在本地运行。

## 活动 3.03:使用模型

### 解决方案

W
wizardforcel 已提交
668
1.  打开用于上一个活动的 Jupyter 笔记本。
W
wizardforcel 已提交
669
2.  复制包含最佳表现模型架构的类,并将其保存在 Python 文件中。确保导入了 PyTorch 所需的库和模块,并将其命名为`final_model.py`。将其命名为`final_model.py`
W
wizardforcel 已提交
670 671 672 673 674

    该文件应如下所示:

    ![Figure 3.19: A screenshot of final_model.py ](img/B15778_03_19.jpg)

W
wizardforcel 已提交
675
    图 3.19:`final_model.py`的屏幕截图
W
wizardforcel 已提交
676

W
wizardforcel 已提交
677
3.  在 Jupyter 笔记本中,保存表现最好的模型。请务必保存与输入单元相关的信息,以及模型的参数。将其命名为`checkpoint.pth`
W
wizardforcel 已提交
678

W
wizardforcel 已提交
679 680 681 682 683
    ```py
    checkpoint = {"input": X_train.shape[1], \
                  "state_dict": model.state_dict()}
    torch.save(checkpoint, "checkpoint.pth")
    ```
W
wizardforcel 已提交
684

W
wizardforcel 已提交
685
4.  打开一个新的 Jupyter 笔记本。
W
wizardforcel 已提交
686
5.  导入 PyTorch,以及我们在“步骤 2”中创建的 Python 文件。
W
wizardforcel 已提交
687

W
wizardforcel 已提交
688 689 690 691
    ```py
    import torch
    import final_model
    ```
W
wizardforcel 已提交
692

W
wizardforcel 已提交
693
6.  创建一个加载模型的函数。
W
wizardforcel 已提交
694

W
wizardforcel 已提交
695 696 697 698 699 700 701 702
    ```py
    def load_model_checkpoint(path):
        checkpoint = torch.load(path)
        model = final_model.Classifier(checkpoint["input"])
        model.load_state_dict(checkpoint["state_dict"])
        return model
    model = load_model_checkpoint("checkpoint.pth")
    ```
W
wizardforcel 已提交
703

W
wizardforcel 已提交
704
7.  通过将以下张量输入到你的模型中进行预测。
W
wizardforcel 已提交
705

W
wizardforcel 已提交
706 707 708 709 710 711 712 713 714 715 716
    ```py
    example = torch.tensor([[0.0606, 0.5000, 0.3333, 0.4828, \
                             0.4000, 0.4000, 0.4000, 0.4000, \
                             0.4000, 0.4000, 0.1651, 0.0869, \
                             0.0980, 0.1825, 0.1054, 0.2807, \
                             0.0016, 0.0000, 0.0033, 0.0027, \
                             0.0031, 0.0021]]).float()
    pred = model(example)
    pred = torch.exp(pred)
    top_p, top_class_test = pred.topk(1, dim=1)
    ```
W
wizardforcel 已提交
717

W
wizardforcel 已提交
718
    通过打印`top_class_test`,我们可以获得模型的预测,在这种情况下,该预测等于`1`(是)。
W
wizardforcel 已提交
719

W
wizardforcel 已提交
720
8.  使用 JIT 模块转换模型。
W
wizardforcel 已提交
721

W
wizardforcel 已提交
722 723 724 725
    ```py
    traced_script = torch.jit.trace(model, example, 
                                    check_trace=False)
    ```
W
wizardforcel 已提交
726

W
wizardforcel 已提交
727
9.  通过输入“步骤 7”的相同张量到模型的跟踪脚本中进行预测。
W
wizardforcel 已提交
728

W
wizardforcel 已提交
729 730 731 732 733
    ```py
    prediction = traced_script(example)
    prediction = torch.exp(prediction)
    top_p_2, top_class_test_2 = prediction.topk(1, dim=1)
    ```
W
wizardforcel 已提交
734

W
wizardforcel 已提交
735
    通过打印`top_class_test_2`,我们从模型的跟踪脚本表示中获得了预测,该预测再次等于`1`(是)。
W
wizardforcel 已提交
736

W
wizardforcel 已提交
737
0.  打开一个新的 Jupyter 笔记本,并导入所需的库来使用 Flask 创建一个 API,以及加载保存的模型的库。
W
wizardforcel 已提交
738

W
wizardforcel 已提交
739 740 741 742 743 744
    ```py
    import flask
    from flask import request
    import torch
    import final_model
    ```
W
wizardforcel 已提交
745

W
wizardforcel 已提交
746
1.  初始化 Flask 应用。
W
wizardforcel 已提交
747

W
wizardforcel 已提交
748 749 750 751
    ```py
    app = flask.Flask(__name__)
    app.config["DEBUG"] = True
    ```
W
wizardforcel 已提交
752

W
wizardforcel 已提交
753
2.  定义一个函数,加载保存的模型,然后实例化模型。
W
wizardforcel 已提交
754

W
wizardforcel 已提交
755 756 757 758 759 760 761 762
    ```py
    def load_model_checkpoint(path):
        checkpoint = torch.load(path)
        model = final_model.Classifier(checkpoint["input"])
        model.load_state_dict(checkpoint["state_dict"])
        return model
    model = load_model_checkpoint("checkpoint.pth")
    ```
W
wizardforcel 已提交
763

W
wizardforcel 已提交
764
3.  定义 API 的路由为`/prediction`,并将方法设置为`POST`。然后,定义接收`POST`数据的函数,并将其反馈给模型进行预测。
W
wizardforcel 已提交
765

W
wizardforcel 已提交
766 767 768 769 770 771 772 773 774 775 776 777
    ```py
    @app.route('/prediction', methods=['POST'])
    def prediction():
        body = request.get_json()
        example = torch.tensor(body['data']).float()
        pred = model(example)
        pred = torch.exp(pred)
        _, top_class_test = pred.topk(1, dim=1)
        top_class_test = top_class_test.numpy()
        
        return {"status":"ok", "result":int(top_class_test[0][0])}
    ```
W
wizardforcel 已提交
778

W
wizardforcel 已提交
779
4.  运行 Flask 应用。
W
wizardforcel 已提交
780

W
wizardforcel 已提交
781 782 783
    ```py
    app.run(debug=True, use_reloader=False)
    ```
W
wizardforcel 已提交
784

W
wizardforcel 已提交
785
    使用为 API 开发而创建的平台 Postman,可以测试 API。 要向 Postman 提交成功的请求,标头的`Content-Type`应当等于`application/json`。 结果输出应如下所示:
W
wizardforcel 已提交
786 787 788

![Figure 3.20: A screenshot of the app after running it ](img/B15778_03_13.jpg)

W
wizardforcel 已提交
789
图 3.20:应用运行后的屏幕截图
W
wizardforcel 已提交
790 791 792

注意

W
wizardforcel 已提交
793
要访问此特定部分的源代码,请参考[这里](https://packt.live/2NHkddn)
W
wizardforcel 已提交
794 795 796 797 798 799 800 801 802

本部分当前没有在线交互示例,需要在本地运行。

# 4.卷积神经网络

## 活动 4.01:针对图像分类问题构建 CNN

### 解决方案

W
wizardforcel 已提交
803
1.  导入所需的库。
W
wizardforcel 已提交
804

W
wizardforcel 已提交
805 806 807 808 809 810 811 812 813 814 815
    ```py
    import numpy as np
    import torch
    from torch import nn, optim
    import torch.nn.functional as F
    from torchvision import datasets
    import torchvision.transforms as transforms
    from torch.utils.data.sampler import SubsetRandomSampler
    from sklearn.metrics import accuracy_score
    import matplotlib.pyplot as plt
    ```
W
wizardforcel 已提交
816

W
wizardforcel 已提交
817
2.  设置要对数据进行的变换,将数据转换为张量并对像素值进行归一化。
W
wizardforcel 已提交
818

W
wizardforcel 已提交
819 820 821 822 823 824
    ```py
    transform = \
        transforms.Compose([transforms.ToTensor(), \
                            transforms.Normalize((0.5, 0.5, 0.5),\
                                                 (0.5, 0.5, 0.5))])
    ```
W
wizardforcel 已提交
825

W
wizardforcel 已提交
826
3.  设置批量大小为 100 张图像,并从 **CIFAR10** 数据集下载训练和测试数据。
W
wizardforcel 已提交
827

W
wizardforcel 已提交
828
    ```py
W
wizardforcel 已提交
829
    batch_size = 100
W
wizardforcel 已提交
830 831 832 833 834 835 836
    train_data = datasets.CIFAR10('data', train=True, \
                                  download=True, \
                                  transform=transform)
    test_data = datasets.CIFAR10('data', train=False, \
                                 download=True, \
                                 transform=transform)
    ```
W
wizardforcel 已提交
837

W
wizardforcel 已提交
838
    前面的代码将下载可通过 PyTorch 的`Torchvision`包获得的训练和测试数据集。 根据上一步中定义的转换对数据集进行转换。
W
wizardforcel 已提交
839

W
wizardforcel 已提交
840
4.  使用 20% 的验证大小,定义训练和验证采样器,用于将数据集划分为这两组。
W
wizardforcel 已提交
841

W
wizardforcel 已提交
842
    ```py
W
wizardforcel 已提交
843
    dev_size = 0.2
W
wizardforcel 已提交
844 845 846 847 848 849 850
    idx = list(range(len(train_data)))
    np.random.shuffle(idx)
    split_size = int(np.floor(dev_size * len(train_data)))
    train_idx, dev_idx = idx[split_size:], idx[:split_size]
    train_sampler = SubsetRandomSampler(train_idx)
    dev_sampler = SubsetRandomSampler(dev_idx)
    ```
W
wizardforcel 已提交
851

W
wizardforcel 已提交
852
    为了将训练集分为两组(训练和验证),为每个组定义了一个索引列表,然后可以使用`SubsetRandomSampler`函数对其进行随机采样。
W
wizardforcel 已提交
853

W
wizardforcel 已提交
854
5.  使用`DataLoader()`函数来定义要使用的每一组数据的批次。
W
wizardforcel 已提交
855

W
wizardforcel 已提交
856
    ```py
W
wizardforcel 已提交
857
    train_loader = \
W
wizardforcel 已提交
858 859 860
    torch.utils.data.DataLoader(train_data, \
                                batch_size=batch_size, \
                                sampler=train_sampler)
W
wizardforcel 已提交
861
    dev_loader = \
W
wizardforcel 已提交
862 863 864
    torch.utils.data.DataLoader(train_data, \
                                batch_size=batch_size, \
                                sampler=dev_sampler)
W
wizardforcel 已提交
865
    test_loader = \
W
wizardforcel 已提交
866 867 868
    torch.utils.data.DataLoader(test_data, \
                                batch_size=batch_size)
    ```
W
wizardforcel 已提交
869

W
wizardforcel 已提交
870
    PyTorch 的`DataLoader`函数用于创建批量,这些批量将在开发过程的训练,验证和测试阶段馈送到模型中。
W
wizardforcel 已提交
871

W
wizardforcel 已提交
872
6.  定义你的网络架构。使用以下信息进行定义。
W
wizardforcel 已提交
873

W
wizardforcel 已提交
874
    Conv1:卷积层,将彩色图像作为输入,并将其通过大小为 3 的 10 个过滤器。应将填充和跨步都设置为 1。
W
wizardforcel 已提交
875 876 877 878 879

    Conv2:一个卷积层,它将输入数据通过大小为 3 的 20 个过滤器传递。填充和跨距都应设置为 1。

    Conv3:一个卷积层,它将输入数据通过大小为 3 的 40 个过滤器传递。填充和跨距都应设置为 1。

W
wizardforcel 已提交
880
    在每个卷积层之后使用 ReLU 激活函数。
W
wizardforcel 已提交
881

W
wizardforcel 已提交
882
    在每个卷积层之后使用池化层,过滤器大小和步幅为 2。
W
wizardforcel 已提交
883

W
wizardforcel 已提交
884
    展平图像后,使用掉落项设置为 20%。
W
wizardforcel 已提交
885

W
wizardforcel 已提交
886
    Linear1:一个全连接层,接收上一层的展平矩阵作为输入,并生成 100 个单元的输出。 为此层使用 ReLU 激活函数。 此处的丢弃期限设置为 20%。
W
wizardforcel 已提交
887

W
wizardforcel 已提交
888
    Linear2:一个全连接层,可生成 10 个输出,每个类标签一个。 将`log_softmax`激活函数用于输出层:
W
wizardforcel 已提交
889

W
wizardforcel 已提交
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
    ```py
    class CNN(nn.Module):
        def __init__(self):
            super(CNN, self).__init__()
            self.conv1 = nn.Conv2d(3, 10, 3, 1, 1)
            self.conv2 = nn.Conv2d(10, 20, 3, 1, 1)
            self.conv3 = nn.Conv2d(20, 40, 3, 1, 1)
            self.pool = nn.MaxPool2d(2, 2)
            self.linear1 = nn.Linear(40 * 4 * 4, 100)
            self.linear2 = nn.Linear(100, 10)
            self.dropout = nn.Dropout(0.2)
        def forward(self, x):
            x = self.pool(F.relu(self.conv1(x)))
            x = self.pool(F.relu(self.conv2(x)))
            x = self.pool(F.relu(self.conv3(x)))
            x = x.view(-1, 40 * 4 * 4)
            x = self.dropout(x)
            x = F.relu(self.linear1(x))
            x = self.dropout(x)
            x = F.log_softmax(self.linear2(x), dim=1)
            return x
    ```
W
wizardforcel 已提交
912

W
wizardforcel 已提交
913
    前面的代码段包含一个定义了网络架构的类(`__init__`方法),以及在信息正向传播过程中所遵循的步骤(`forward`方法)。
W
wizardforcel 已提交
914

W
wizardforcel 已提交
915
7.  定义训练模型所需的所有参数。设置周期数为`50`
W
wizardforcel 已提交
916

W
wizardforcel 已提交
917 918 919 920 921 922
    ```py
    model = CNN()
    loss_function = nn.NLLLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    epochs = 50
    ```
W
wizardforcel 已提交
923 924 925 926 927

    我们为此练习选择的优化器是 Adam。 同样,负对数似然率用作损失函数,如本书前一章所述。

    如果您的计算机具有可用的 GPU,则应按以下步骤完成模型的实例化:

W
wizardforcel 已提交
928 929 930
    ```py
    model = CNN().to("cuda")
    ```
W
wizardforcel 已提交
931

W
wizardforcel 已提交
932
8.  训练你的网络,并确保保存训练集和验证集的损失和准确率的值。
W
wizardforcel 已提交
933

W
wizardforcel 已提交
934 935
    ```py
    train_losses, dev_losses, train_acc, dev_acc= [], [], [], []
W
wizardforcel 已提交
936
    x_axis = []
W
wizardforcel 已提交
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
    # For loop through the epochs
    for e in range(1, epochs+1):
        losses = 0
        acc = 0
        iterations = 0
        model.train()
        """
        For loop through the batches (created using
        the train loader)
        """
        for data, target in train_loader:
            iterations += 1
            # Forward and backward pass of the training data
            pred = model(data)
            loss = loss_function(pred, target)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            losses += loss.item()
            p = torch.exp(pred)
            top_p, top_class = p.topk(1, dim=1)
            acc += accuracy_score(target, top_class)
        dev_losss = 0
        dev_accs = 0
        iter_2 = 0
        # Validation of model for given epoch
        if e%5 == 0 or e == 1:
            x_axis.append(e)
            with torch.no_grad():
                model.eval()
                """
                For loop through the batches of
                the validation set
                """
                for data_dev, target_dev in dev_loader:
                    iter_2 += 1
                    dev_pred = model(data_dev)
                    dev_loss = loss_function(dev_pred, target_dev)
                    dev_losss += dev_loss.item()
                    dev_p = torch.exp(dev_pred)
                    top_p, dev_top_class = dev_p.topk(1, dim=1)
                    dev_accs += accuracy_score(target_dev, \
                                               dev_top_class)
            # Losses and accuracy are appended to be printed
            train_losses.append(losses/iterations)
            dev_losses.append(dev_losss/iter_2)
            train_acc.append(acc/iterations)
            dev_acc.append(dev_accs/iter_2)
            print("Epoch: {}/{}.. ".format(e, epochs), \
                  "Training Loss: {:.3f}.. "\
                  .format(losses/iterations), \
                  "Validation Loss: {:.3f}.. "\
                  .format(dev_losss/iter_2), \
                  "Training Accuracy: {:.3f}.. "\
                  .format(acc/iterations), \
                  "Validation Accuracy: {:.3f}"\
                  .format(dev_accs/iter_2))
    ```
W
wizardforcel 已提交
995 996 997

    如果您的计算机具有可用的 GPU,则对前面的代码进行一些修改,如下所示:

W
wizardforcel 已提交
998 999
    ```py
    train_losses, dev_losses, train_acc, dev_acc= [], [], [], []
W
wizardforcel 已提交
1000
    x_axis = []
W
wizardforcel 已提交
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
    # For loop through the epochs
    for e in range(1, epochs+1):
        losses = 0
        acc = 0
        iterations = 0
        
        model.train()
        """
        For loop through the batches
        (created using the train loader)
        """
        for data, target in train_loader:
            iterations += 1
            # Forward and backward pass of the training data
            pred = model(data.to("cuda"))
            loss = loss_function(pred, target.to("cuda"))
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            losses += loss.item()
            p = torch.exp(pred)
            top_p, top_class = p.topk(1, dim=1)
            acc += accuracy_score(target.to("cpu"), \
                   top_class.to("cpu"))
        dev_losss = 0
        dev_accs = 0
        iter_2 = 0
        # Validation of model for given epoch
        if e%5 == 0 or e == 1:
            x_axis.append(e)
            with torch.no_grad():
                model.eval()
                """
                For loop through the batches of
                the validation set
                """
                for data_dev, target_dev in dev_loader:
                    iter_2 += 1
                    dev_pred = model(data_dev.to("cuda"))
                    dev_loss = loss_function(dev_pred, \
                               target_dev.to("cuda"))
                    dev_losss += dev_loss.item()
                    dev_p = torch.exp(dev_pred)
                    top_p, dev_top_class = dev_p.topk(1, dim=1)
                    dev_accs += \
                    accuracy_score(target_dev.to("cpu"), \
                                   dev_top_class.to("cpu"))
            # Losses and accuracy are appended to be printed
            train_losses.append(losses/iterations)
            dev_losses.append(dev_losss/iter_2)
            train_acc.append(acc/iterations)
            dev_acc.append(dev_accs/iter_2)
            print("Epoch: {}/{}.. ".format(e, epochs), \
                  "Training Loss: {:.3f}.. "\
                  .format(losses/iterations), \
                  "Validation Loss: {:.3f}.. "\
                  .format(dev_losss/iter_2), \
                  "Training Accuracy: {:.3f}.. "\
                  .format(acc/iterations), \
                  "Validation Accuracy: {:.3f}"\
                  .format(dev_accs/iter_2))
    ```
W
wizardforcel 已提交
1063

W
wizardforcel 已提交
1064
9.  绘制这两组数据的损失和精度。要绘制损失,请使用以下代码。
W
wizardforcel 已提交
1065

W
wizardforcel 已提交
1066 1067 1068 1069
    ```py
    plt.plot(x_axis,train_losses, label='Training loss')
    plt.plot(x_axis, dev_losses, label='Validation loss')
    plt.legend(frameon=False)
W
wizardforcel 已提交
1070
    plt.show()
W
wizardforcel 已提交
1071
    ```
W
wizardforcel 已提交
1072 1073 1074 1075 1076

    结果图应类似于以下内容:

    ![Figure 4.23: Resulting plot showing the loss of the sets ](img/B15778_04_23.jpg)

W
wizardforcel 已提交
1077
    图 4.23:结果图显示了集合的损失
W
wizardforcel 已提交
1078 1079 1080

    要绘制精度,请使用以下代码:

W
wizardforcel 已提交
1081 1082 1083 1084
    ```py
    plt.plot(x_axis, train_acc, label="Training accuracy")
    plt.plot(x_axis, dev_acc, label="Validation accuracy")
    plt.legend(frameon=False)
W
wizardforcel 已提交
1085
    plt.show()
W
wizardforcel 已提交
1086
    ```
W
wizardforcel 已提交
1087 1088 1089 1090 1091

    该图应类似于以下内容:

    ![Figure 4.24: Resulting plot showing the accuracy of the sets ](img/B15778_04_24.jpg)

W
wizardforcel 已提交
1092
    图 4.24:结果图显示了集合的准确率
W
wizardforcel 已提交
1093

W
wizardforcel 已提交
1094
    可以看出,在第 15 个周期之后,过拟合开始影响模型。
W
wizardforcel 已提交
1095

W
wizardforcel 已提交
1096
0.  在测试集上检查模型的准确率。
W
wizardforcel 已提交
1097

W
wizardforcel 已提交
1098 1099
    ```py
    model.eval()
W
wizardforcel 已提交
1100 1101
    iter_3 = 0
    acc_test = 0
W
wizardforcel 已提交
1102 1103 1104 1105 1106 1107 1108 1109
    for data_test, target_test in test_loader:
        iter_3 += 1
        test_pred = model(data_test)
        test_pred = torch.exp(test_pred)
        top_p, top_class_test = test_pred.topk(1, dim=1)
        acc_test += accuracy_score(target_test, top_class_test)
    print(acc_test/iter_3)
    ```
W
wizardforcel 已提交
1110

W
wizardforcel 已提交
1111
    使用我们之前创建的数据加载器,可以对测试集数据进行图像分类,以估计模型在看不见数据上的准确率。
W
wizardforcel 已提交
1112 1113 1114

    如果您的计算机具有可用的 GPU,则对前面的代码进行一些修改,如下所示:

W
wizardforcel 已提交
1115 1116
    ```py
    model.eval()
W
wizardforcel 已提交
1117 1118
    iter_3 = 0
    acc_test = 0
W
wizardforcel 已提交
1119 1120 1121 1122 1123 1124 1125 1126 1127
    for data_test, target_test in test_loader:
        iter_3 += 1
        test_pred = model(data_test.to("cuda"))
        test_pred = torch.exp(test_pred)
        top_p, top_class_test = test_pred.topk(1, dim=1)
        acc_test += accuracy_score(target_test .to("cpu"), \
                                   top_class_test .to("cpu"))
    print(acc_test/iter_3)
    ```
W
wizardforcel 已提交
1128

W
wizardforcel 已提交
1129
    测试集的准确率与其他两组所达到的准确率非常相似,这意味着该模型能够对看不见的数据表现出同样出色的表现。 它应该在 72% 左右。
W
wizardforcel 已提交
1130 1131 1132

    注意

W
wizardforcel 已提交
1133
    要访问此特定部分的源代码,请参考[这里](https://packt.live/3gjvWuV)。
W
wizardforcel 已提交
1134 1135 1136

    本部分当前没有在线交互示例,需要在本地运行。

W
wizardforcel 已提交
1137
    要访问此源代码的 GPU 版本,请参考[这里](https://packt.live/2BUGjGF)。 此版本的源代码无法作为在线交互示例使用,需要通过 GPU 设置在本地运行。
W
wizardforcel 已提交
1138

W
wizardforcel 已提交
1139
## 活动 4.02:实现数据扩充
W
wizardforcel 已提交
1140 1141 1142

### 解决方案

W
wizardforcel 已提交
1143
1.  复制之前活动中的笔记本。
W
wizardforcel 已提交
1144

W
wizardforcel 已提交
1145
    为了完成此活动,按照以下步骤,除了修改`tranforms`值之外,不会更改任何代码。
W
wizardforcel 已提交
1146

W
wizardforcel 已提交
1147
2.  修改`transform`变量的定义,使其除了对数据进行归一化和转换为张量外,还包括以下转换:
W
wizardforcel 已提交
1148

W
wizardforcel 已提交
1149
    对于训练/验证集,请使用`RandomHorizo​​ntalFlip`函数,其概率为 50%(`0.5`),并使用`RandomGrayscale`函数,其概率为 10%(`0.1`)。
W
wizardforcel 已提交
1150 1151 1152

    对于测试集,请勿添加任何其他转换:

W
wizardforcel 已提交
1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165
    ```py
    transform = \
    {"train": transforms.Compose([\
              transforms.RandomHorizontalFlip(0.5), \
              transforms.RandomGrayscale(0.1),\
              transforms.ToTensor(),\
              transforms.Normalize((0.5, 0.5, 0.5), \
                                   (0.5, 0.5, 0.5))]),\
    "test": transforms.Compose([\
            transforms.ToTensor(),\
            transforms.Normalize((0.5, 0.5, 0.5), \
                                 (0.5, 0.5, 0.5))])}
    ```
W
wizardforcel 已提交
1166

W
wizardforcel 已提交
1167
3.  训练模型 100 个周期。
W
wizardforcel 已提交
1168 1169 1170

    如果您的计算机具有可用的 GPU,请确保使用代码的 GPU 版本来训练模型。

W
wizardforcel 已提交
1171
    在训练和验证集上得出的损失和准确率图应与此处显示的图相似:
W
wizardforcel 已提交
1172 1173 1174

    ![Figure 4.25: Resulting plot showing the loss of the sets ](img/B15778_04_25.jpg)

W
wizardforcel 已提交
1175
    图 4.25:结果图显示了集合的损失
W
wizardforcel 已提交
1176 1177 1178

    ![Figure 4.26: Resulting plot showing the accuracy of the sets ](img/B15778_04_26.jpg)

W
wizardforcel 已提交
1179
    图 4.26:结果图显示了集合的准确率
W
wizardforcel 已提交
1180

W
wizardforcel 已提交
1181
    通过添加数据扩充,可以改善模型的表现,并减少发生的过拟合。
W
wizardforcel 已提交
1182

W
wizardforcel 已提交
1183
4.  计算所得模型在测试集上的精度。
W
wizardforcel 已提交
1184

W
wizardforcel 已提交
1185
    该模型在测试设备上的表现提高了约 75%。
W
wizardforcel 已提交
1186 1187 1188

    注意

W
wizardforcel 已提交
1189
    要访问此特定部分的源代码,请参考[这里](https://packt.live/3ePcAND)。
W
wizardforcel 已提交
1190 1191 1192

    本部分当前没有在线交互示例,需要在本地运行。

W
wizardforcel 已提交
1193
    要访问此源代码的 GPU 版本,请参考[这里](https://packt.live/38jpq4g)。 此版本的源代码无法作为在线交互示例使用,需要通过 GPU 设置在本地运行。
W
wizardforcel 已提交
1194 1195 1196 1197 1198

## 活动 4.03:实现批量标准化

### 解决方案

W
wizardforcel 已提交
1199
1.  复制之前活动中的笔记本。
W
wizardforcel 已提交
1200

W
wizardforcel 已提交
1201
    要完成此活动,按照以下步骤,除了在网络架构中添加一些层之外,不会更改任何代码。
W
wizardforcel 已提交
1202

W
wizardforcel 已提交
1203
2.  将批量归一化添加到每个卷积层,以及第一个全连接层。
W
wizardforcel 已提交
1204 1205 1206

    网络的最终架构应如下:

W
wizardforcel 已提交
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232
    ```py
    class CNN(nn.Module):
        def __init__(self):
            super(Net, self).__init__()
            self.conv1 = nn.Conv2d(3, 10, 3, 1, 1)
            self.norm1 = nn.BatchNorm2d(10)
            self.conv2 = nn.Conv2d(10, 20, 3, 1, 1)
            self.norm2 = nn.BatchNorm2d(20)
            self.conv3 = nn.Conv2d(20, 40, 3, 1, 1)
            self.norm3 = nn.BatchNorm2d(40)
            self.pool = nn.MaxPool2d(2, 2)
            self.linear1 = nn.Linear(40 * 4 * 4, 100)
            self.norm4 = nn.BatchNorm1d(100)
            self.linear2 = nn.Linear(100, 10)
            self.dropout = nn.Dropout(0.2)
        def forward(self, x):
            x = self.pool(self.norm1(F.relu(self.conv1(x))))
            x = self.pool(self.norm2(F.relu(self.conv2(x))))
            x = self.pool(self.norm3(F.relu(self.conv3(x))))
            x = x.view(-1, 40 * 4 * 4)
            x = self.dropout(x)
            x = self.norm4(F.relu(self.linear1(x)))
            x = self.dropout(x)
            x = F.log_softmax(self.linear2(x), dim=1)
            return x
    ```
W
wizardforcel 已提交
1233

W
wizardforcel 已提交
1234
3.  训练模型 100 个周期。
W
wizardforcel 已提交
1235

W
wizardforcel 已提交
1236
    如果您的计算机具有可用的 GPU,请确保使用代码的 GPU 版本来训练模型。 训练和验证集的损失和准确率的结果图应类似于此处所示:
W
wizardforcel 已提交
1237 1238 1239

    ![Figure 4.27: Resulting plot showing the loss of the sets ](img/B15778_04_27.jpg)

W
wizardforcel 已提交
1240
    图 4.27:结果图显示集合的损失
W
wizardforcel 已提交
1241 1242 1243

    ![Figure 4.28: Resulting plot showing the loss of the sets ](img/B15778_04_28.jpg)

W
wizardforcel 已提交
1244
    图 4.28:结果图显示集合的损失
W
wizardforcel 已提交
1245

W
wizardforcel 已提交
1246
    尽管过拟合再次引入了模型,但是我们可以看到两组的表现都有所提高。
W
wizardforcel 已提交
1247 1248 1249

    注意

W
wizardforcel 已提交
1250
    尽管本章未对此进行探讨,但理想的步骤是为网络架构添加丢弃以减少高方差。 随意尝试一下,看看您是否能够进一步提高性能。
W
wizardforcel 已提交
1251

W
wizardforcel 已提交
1252
4.  计算所得模型在测试集上的精度。
W
wizardforcel 已提交
1253

W
wizardforcel 已提交
1254
    该模型在测试设备上的表现已提高了约 78%。
W
wizardforcel 已提交
1255 1256 1257

    注意

W
wizardforcel 已提交
1258
    要访问此特定部分的源代码,请参考[这里](https://packt.live/31sSR2G)。
W
wizardforcel 已提交
1259 1260 1261

    本部分当前没有在线交互示例,需要在本地运行。

W
wizardforcel 已提交
1262
    要访问此源代码的 GPU 版本,请参考[这里](https://packt.live/3eVgp4g)。 此版本的源代码无法作为在线交互示例使用,需要通过 GPU 设置在本地运行。
W
wizardforcel 已提交
1263

W
wizardforcel 已提交
1264
# 5.样式迁移
W
wizardforcel 已提交
1265

W
wizardforcel 已提交
1266
## 活动 5.01:执行样式迁移
W
wizardforcel 已提交
1267 1268 1269

### 解决方案

W
wizardforcel 已提交
1270
1.  导入所需的库。
W
wizardforcel 已提交
1271

W
wizardforcel 已提交
1272 1273 1274 1275 1276 1277 1278 1279
    ```py
    import numpy as np
    import torch
    from torch import nn, optim
    from PIL import Image
    import matplotlib.pyplot as plt
    from torchvision import transforms, models
    ```
W
wizardforcel 已提交
1280

W
wizardforcel 已提交
1281
    如果您的计算机具有可用的 GPU,请确保定义一个名为`device`的变量,该变量将有助于为 GPU 分配一些变量,如下所示:
W
wizardforcel 已提交
1282

W
wizardforcel 已提交
1283
    ```py
W
wizardforcel 已提交
1284
    device = "cuda"
W
wizardforcel 已提交
1285
    ```
W
wizardforcel 已提交
1286

W
wizardforcel 已提交
1287
2.  指定要对输入图像进行的变换。请确保将它们调整为相同的大小,将它们转换为张力,并将它们标准化。
W
wizardforcel 已提交
1288

W
wizardforcel 已提交
1289 1290 1291 1292 1293 1294 1295 1296
    ```py
    imsize = 224
    loader = \
    transforms.Compose([transforms.Resize(imsize), \
                        transforms.ToTensor(),\
                        transforms.Normalize((0.485, 0.456, 0.406), \
                                             (0.229, 0.224, 0.225))])
    ```
W
wizardforcel 已提交
1297

W
wizardforcel 已提交
1298
3.  定义一个图像加载器函数。它应该打开图像并加载它。调用图像加载器函数来加载两个输入图像。
W
wizardforcel 已提交
1299

W
wizardforcel 已提交
1300 1301 1302 1303 1304 1305 1306 1307
    ```py
    def image_loader(image_name):
        image = Image.open(image_name)
        image = loader(image).unsqueeze(0)
        return image
    content_img = image_loader("images/landscape.jpg")
    style_img = image_loader("images/monet.jpg")
    ```
W
wizardforcel 已提交
1308 1309 1310

    如果您的计算机有可用的 GPU,请改用以下代码段:

W
wizardforcel 已提交
1311 1312 1313 1314 1315 1316 1317 1318
    ```py
    def image_loader(image_name):
        image = Image.open(image_name)
        image = loader(image).unsqueeze(0)
        return image
    content_img = image_loader("images/landscape.jpg").to(device)
    style_img = image_loader("images/monet.jpg").to(device)
    ```
W
wizardforcel 已提交
1319

W
wizardforcel 已提交
1320
4.  为了能够显示图像,设置变换以恢复图像的归一化,并将张量转换为`PIL`图像。
W
wizardforcel 已提交
1321

W
wizardforcel 已提交
1322 1323 1324 1325 1326 1327 1328 1329
    ```py
    unloader = transforms.Compose([\
               transforms.Normalize((-0.485/0.229, \
                                     -0.456/0.224, \
                                     -0.406/0.225), \
                                    (1/0.229, 1/0.224, 1/0.225)),\
               transforms.ToPILImage()])
    ```
W
wizardforcel 已提交
1330

W
wizardforcel 已提交
1331
5.  创建一个函数(`tensor2image`),它能够在张量上执行前面的变换。对两幅图像调用该函数并绘制结果。
W
wizardforcel 已提交
1332

W
wizardforcel 已提交
1333 1334 1335 1336 1337 1338 1339 1340 1341
    ```py
    def tensor2image(tensor):
        image = tensor.clone()
        image = image.squeeze(0)
        image = unloader(image)
        return image
    plt.figure()
    plt.imshow(tensor2image(content_img))
    plt.title("Content Image")
W
wizardforcel 已提交
1342
    plt.show()
W
wizardforcel 已提交
1343 1344 1345
    plt.figure()
    plt.imshow(tensor2image(style_img))
    plt.title("Style Image")
W
wizardforcel 已提交
1346
    plt.show()
W
wizardforcel 已提交
1347
    ```
W
wizardforcel 已提交
1348 1349 1350

    如果您的计算机有可用的 GPU,请改用以下代码段:

W
wizardforcel 已提交
1351 1352 1353 1354 1355 1356 1357 1358 1359
    ```py
    def tensor2image(tensor):
        image = tensor.to("cpu").clone()
        image = image.squeeze(0)
        image = unloader(image)
        return image
    plt.figure()
    plt.imshow(tensor2image(content_img))
    plt.title("Content Image")
W
wizardforcel 已提交
1360
    plt.show()
W
wizardforcel 已提交
1361 1362 1363
    plt.figure()
    plt.imshow(tensor2image(style_img))
    plt.title("Style Image")
W
wizardforcel 已提交
1364
    plt.show()
W
wizardforcel 已提交
1365
    ```
W
wizardforcel 已提交
1366

W
wizardforcel 已提交
1367
6.  加载 VGG-19 模型。
W
wizardforcel 已提交
1368

W
wizardforcel 已提交
1369 1370 1371 1372 1373
    ```py
    model = models.vgg19(pretrained=True).features
    for param in model.parameters():
        param.requires_grad_(False)
    ```
W
wizardforcel 已提交
1374

W
wizardforcel 已提交
1375
7.  创建一个字典,用于将相关层的索引(键)映射到名称(值)。然后,创建一个函数来提取相关层的特征映射。用它们来提取两个输入图像的特征。
W
wizardforcel 已提交
1376 1377 1378

    以下函数应为每个相关层提取给定图像的特征:

W
wizardforcel 已提交
1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390
    ```py
    relevant_layers = {'0': 'conv1_1', '5': 'conv2_1', \
                       '10': 'conv3_1', '19': 'conv4_1', \
                       '21': 'conv4_2', '28': 'conv5_1'}
    def features_extractor(x, model, layers):
        features = {}
        for index, layer in model._modules.items():
            x = layer(x)
            if index in layers:
                features[layers[index]] = x
        return features
    ```
W
wizardforcel 已提交
1391 1392

    接下来,应该为**内容**和**样式**图像调用该函数:
W
wizardforcel 已提交
1393 1394 1395 1396 1397 1398 1399 1400
    
    ```py
    content_features = features_extractor(content_img, \
                                          model, \
                                          relevant_layers)
    style_features = features_extractor(style_img, model, \
                                        relevant_layers)
    ```
W
wizardforcel 已提交
1401

W
wizardforcel 已提交
1402
8.  计算风格特征的 Gram 矩阵。同时,创建初始目标图像。
W
wizardforcel 已提交
1403

W
wizardforcel 已提交
1404
    以下代码段为用于提取样式特征的每个层创建了 gram 矩阵:
W
wizardforcel 已提交
1405

W
wizardforcel 已提交
1406
    ```py
W
wizardforcel 已提交
1407
    style_grams = {}
W
wizardforcel 已提交
1408 1409 1410 1411 1412 1413 1414
    for i in style_features:
        layer = style_features[i]
        _, d1, d2, d3 = layer.shape
        features = layer.view(d1, d2 * d3)
        gram = torch.mm(features, features.t())
        style_grams[i] = gram
    ```
W
wizardforcel 已提交
1415 1416 1417

    接下来,创建初始目标图像作为内容图像的克隆:

W
wizardforcel 已提交
1418 1419 1420
    ```py
    target_img = content_img.clone().requires_grad_(True)
    ```
W
wizardforcel 已提交
1421 1422 1423

    如果您的计算机有可用的 GPU,请改用以下代码段:

W
wizardforcel 已提交
1424 1425 1426
    ```py
    target_img = content_img.clone().requires_grad_(True).to(device)
    ```
W
wizardforcel 已提交
1427

W
wizardforcel 已提交
1428
9.  设置不同样式层的权重,以及内容和样式损失的权重。
W
wizardforcel 已提交
1429

W
wizardforcel 已提交
1430 1431 1432 1433 1434 1435 1436
    ```py
    style_weights = {'conv1_1': 1., 'conv2_1': 0.8, \
                     'conv3_1': 0.6, 'conv4_1': 0.4, \
                     'conv5_1': 0.2}
    alpha = 1
    beta = 1e5
    ```
W
wizardforcel 已提交
1437

W
wizardforcel 已提交
1438
0.  运行模型 500 次迭代。在开始训练模型之前,定义 Adam 优化算法,以`0.001`作为学习率。
W
wizardforcel 已提交
1439 1440 1441 1442 1443

    注意

    为了获得本书所示的最终目标图像,该代码运行了 5,000 次迭代,而没有 GPU 则需要很长时间才能运行。 但是,要欣赏输出图像中开始发生的更改,尽管鼓励您测试不同的训练时间,但只需运行 500 次迭代就足够了。

W
wizardforcel 已提交
1444
    ```py
W
wizardforcel 已提交
1445
    print_statement = 500
W
wizardforcel 已提交
1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482
    optimizer = torch.optim.Adam([target_img], lr=0.001)
    iterations = 5000
    for i in range(1, iterations+1):
        # Extract features for all relevant layers
        target_features = features_extractor(target_img, model, \
                                             relevant_layers)
        # Calculate the content loss
        content_loss = torch.mean((target_features['conv4_2'] \
                                   - content_features['conv4_2'])**2)
        # Loop through all style layers
        style_losses = 0
        for layer in style_weights:
            # Create gram matrix for that layer
            target_feature = target_features[layer]
            _, d1, d2, d3 = target_feature.shape
            target_reshaped = target_feature.view(d1, d2 * d3)
            target_gram = torch.mm(target_reshaped, \
                                   target_reshaped.t())
            style_gram = style_grams[layer]
            # Calculate style loss for that layer
            style_loss = style_weights[layer] * \
                         torch.mean((target_gram - \
                                     style_gram)**2)
            #Calculate style loss for all layers
            style_losses += style_loss / (d1 * d2 * d3)
        # Calculate the total loss
        total_loss = alpha * content_loss + beta * style_losses
        # Perform back propagation
        optimizer.zero_grad()
        total_loss.backward()
        optimizer.step()
        # Print the target image
        if i % print_statement == 0 or i == 1:
            print('Total loss: ', total_loss.item())
            plt.imshow(tensor2image(target_img))
            plt.show()
    ```
W
wizardforcel 已提交
1483

W
wizardforcel 已提交
1484
1.  绘制**内容****风格****目标**的图片,比较结果。
W
wizardforcel 已提交
1485

W
wizardforcel 已提交
1486 1487 1488 1489 1490
    ```py
    fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 5))
    ax1.imshow(tensor2image(content_img))
    ax2.imshow(tensor2image(target_img))
    ax3.imshow(tensor2image(style_img))
W
wizardforcel 已提交
1491
    plt.show()
W
wizardforcel 已提交
1492
    ```
W
wizardforcel 已提交
1493 1494 1495 1496 1497 1498 1499 1500 1501

    从此代码段派生的图应类似于此处显示的图:

![Figure 5.11: Output plots ](img/B15778_05_11.jpg)

图 5.11:输出图

注意

W
wizardforcel 已提交
1502
要查看高质量彩色图像,请访问本书的 GitHub 存储库,网址为 https://packt.live/2KcORcw。
W
wizardforcel 已提交
1503

W
wizardforcel 已提交
1504
要访问此特定部分的源代码,请参考[这里](https://packt.live/2BZj91B)
W
wizardforcel 已提交
1505 1506 1507

本部分当前没有在线交互示例,需要在本地运行。

W
wizardforcel 已提交
1508
要访问此源代码的 GPU 版本,请参考[这里](https://packt.live/3eNfvqc)。 此版本的源代码无法作为在线交互示例使用,需要通过 GPU 设置在本地运行。
W
wizardforcel 已提交
1509 1510 1511

# 6.使用 RNN 分析数据序列

W
wizardforcel 已提交
1512
## 活动 6.01:使用简单 RNN 的时间序列预测
W
wizardforcel 已提交
1513 1514 1515

### 解决方案

W
wizardforcel 已提交
1516
1.  导入所需的库,具体如下:
W
wizardforcel 已提交
1517

W
wizardforcel 已提交
1518 1519 1520 1521 1522 1523
    ```py
    import pandas as pd
    import matplotlib.pyplot as plt
    import torch
    from torch import nn, optim
    ```
W
wizardforcel 已提交
1524

W
wizardforcel 已提交
1525
2.  加载数据集,然后对其进行切片,使其包含所有的行,但只包含索引 1 到 52 的列。
W
wizardforcel 已提交
1526

W
wizardforcel 已提交
1527 1528 1529 1530 1531
    ```py
    data = pd.read_csv("Sales_Transactions_Dataset_Weekly.csv")
    data = data.iloc[:,1:53]
    data.head()
    ```
W
wizardforcel 已提交
1532 1533 1534 1535 1536 1537 1538

    输出如下:

    ![Figure 6.26: Displaying dataset for columns from index 1 to 52 ](img/B15778_06_26.jpg)

    图 6.26:显示索引 1 到 52 列的数据集

W
wizardforcel 已提交
1539
3.  绘制从整个数据集中随机选择的五种产品的每周销售交易情况。在进行随机采样时,使用随机种子`0`,以达到与当前活动相同的结果。
W
wizardforcel 已提交
1540

W
wizardforcel 已提交
1541 1542 1543 1544 1545 1546 1547 1548 1549
    ```py
    plot_data = data.sample(5, random_state=0)
    x = range(1,53)
    plt.figure(figsize=(10,5))
    for i,row in plot_data.iterrows():
        plt.plot(x,row)
    plt.legend(plot_data.index)
    plt.xlabel("Weeks")
    plt.ylabel("Sales transactions per product")
W
wizardforcel 已提交
1550
    plt.show()
W
wizardforcel 已提交
1551
    ```
W
wizardforcel 已提交
1552 1553 1554 1555 1556 1557 1558

    结果图应如下所示:

    ![Figure 6.27: Plot of the output ](img/B15778_06_27.jpg)

    图 6.27:输出图

W
wizardforcel 已提交
1559
4.  创建`input``target`变量,这些变量将被输入到网络中以创建模型。这些变量应具有相同的形状,并转换为 PyTorch 张量。
W
wizardforcel 已提交
1560

W
wizardforcel 已提交
1561
    `input`变量应包含除上周外的所有星期的所有产品数据,因为模型的目的是预测最后一周。
W
wizardforcel 已提交
1562

W
wizardforcel 已提交
1563
    `target`变量应比`input`变量领先一步; 也就是说,`target`变量的第一个值应该是输入变量中的第二个,依此类推,直到`target`变量的最后一个值(应该被留在`input`变量之外):
W
wizardforcel 已提交
1564

W
wizardforcel 已提交
1565 1566 1567 1568 1569 1570 1571 1572
    ```py
    data_train = data.iloc[:,:-1]
    inputs = torch.Tensor(data_train.values).unsqueeze(1)
    targets = data_train.shift(-1, axis="columns", \
                               fill_value=data.iloc[:,-1])\
                               .astype(dtype = "float32")
    targets = torch.Tensor(targets.values)
    ```
W
wizardforcel 已提交
1573

W
wizardforcel 已提交
1574
5.  创建一个包含网络架构的类。注意,全连接层的输出大小应该是`1`
W
wizardforcel 已提交
1575

W
wizardforcel 已提交
1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589
    ```py
    class RNN(nn.Module):
        def __init__(self, input_size, hidden_size, num_layers):
            super().__init__()
            self.hidden_size = hidden_size
            self.rnn = nn.RNN(input_size, hidden_size, \
                              num_layers, batch_first=True)
            self.output = nn.Linear(hidden_size, 1)
        def forward(self, x, hidden):
            out, hidden = self.rnn(x, hidden)
            out = out.view(-1, self.hidden_size)
            out = self.output(out)
            return out, hidden
    ```
W
wizardforcel 已提交
1590

W
wizardforcel 已提交
1591
    与之前的活动一样,该类包含`__init__`方法以及网络架构,以及`forward`方法,该方法确定信息在各层之间的流动。
W
wizardforcel 已提交
1592

W
wizardforcel 已提交
1593
6.  实例化包含模型的**类**函数。输入输入大小、每个循环层的神经元数量(`10`)和循环层数量(`1`)。
W
wizardforcel 已提交
1594

W
wizardforcel 已提交
1595 1596 1597 1598
    ```py
    model = RNN(data_train.shape[1], 10, 1)
    model
    ```
W
wizardforcel 已提交
1599 1600 1601

    运行前面的代码将显示以下输出:

W
wizardforcel 已提交
1602 1603 1604 1605
    ```py
    RNN(
      (rnn): RNN(51, 10, batch_first=True)
      (output): Linear(in_features=10, out_features=1, bias=True)
W
wizardforcel 已提交
1606
    )
W
wizardforcel 已提交
1607
    ```
W
wizardforcel 已提交
1608

W
wizardforcel 已提交
1609
7.  定义一个损失函数,一个优化算法,以及训练网络的周期数。使用 MSE 损失函数、Adam 优化器和 10,000 个周期来完成这一任务。
W
wizardforcel 已提交
1610

W
wizardforcel 已提交
1611 1612 1613 1614 1615
    ```py
    loss_function = nn.MSELoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    epochs = 10000
    ```
W
wizardforcel 已提交
1616

W
wizardforcel 已提交
1617
8.  使用`for`循环来执行训练过程,经历所有的周期。在每个周期中,必须进行预测,以及随后的损失函数计算和网络参数的优化。保存每个周期的损失。
W
wizardforcel 已提交
1618 1619 1620

    注意

W
wizardforcel 已提交
1621
    考虑到没有批量用于遍历数据集,`hidden`量实际上并未在批量之间传递(而是在处理序列的每个元素时使用隐藏状态),但是为了清楚起见,它留在这里。
W
wizardforcel 已提交
1622

W
wizardforcel 已提交
1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636
    ```py
    losses = []
    for i in range(1, epochs+1):
        hidden = None
        pred, hidden = model(inputs, hidden)
        target = targets[:,-1].unsqueeze(1)
        loss = loss_function(targets, pred)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        losses.append(loss.item())
        if i%1000 == 0:
            print("epoch: ", i, "=... Loss function: ", losses[-1])
    ```
W
wizardforcel 已提交
1637 1638 1639

    输出应如下所示:

W
wizardforcel 已提交
1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651
    ```py
    epoch: 1000 ... Loss function: 58.48879623413086
    epoch: 2000 ... Loss function: 24.934917449951172
    epoch: 3000 ... Loss function: 13.247632026672363
    epoch: 4000 ... Loss function: 9.884735107421875
    epoch: 5000 ... Loss function: 8.778228759765625
    epoch: 6000 ... Loss function: 8.025042533874512
    epoch: 7000 ... Loss function: 7.622503757476807
    epoch: 8000 ... Loss function: 7.4796295166015625
    epoch: 9000 ... Loss function: 7.351718902587891
    epoch: 10000 ... Loss function: 7.311776161193848
    ```
W
wizardforcel 已提交
1652

W
wizardforcel 已提交
1653
9.  将所有周期的损失绘制如下:
W
wizardforcel 已提交
1654

W
wizardforcel 已提交
1655 1656 1657 1658 1659
    ```py
    x_range = range(len(losses))
    plt.plot(x_range, losses)
    plt.xlabel("epochs")
    plt.ylabel("Loss function")
W
wizardforcel 已提交
1660
    plt.show()
W
wizardforcel 已提交
1661
    ```
W
wizardforcel 已提交
1662 1663 1664 1665 1666

    结果图应如下所示:

    ![Figure 6.28: Plot displaying the losses of all epochs ](img/B15778_06_28.jpg)

W
wizardforcel 已提交
1667
    图 6.28:显示所有周期的损失的图
W
wizardforcel 已提交
1668

W
wizardforcel 已提交
1669
0.  使用散点图,显示在训练过程的最后一个周期中获得的预测值与真实情况值(即上周的销售交易)的对比。
W
wizardforcel 已提交
1670

W
wizardforcel 已提交
1671 1672 1673 1674 1675 1676 1677 1678 1679 1680
    ```py
    x_range = range(len(data))
    target = data.iloc[:,-1].values.reshape(len(data),1)
    plt.figure(figsize=(15,5))
    plt.scatter(x_range[:20], target[:20])
    plt.scatter(x_range[:20], pred.detach().numpy()[:20])
    plt.legend(["Ground truth", "Prediction"])
    plt.xlabel("Product")
    plt.ylabel("Sales Transactions")
    plt.xticks(range(0, 20))
W
wizardforcel 已提交
1681
    plt.show()
W
wizardforcel 已提交
1682
    ```
W
wizardforcel 已提交
1683 1684 1685 1686 1687 1688 1689 1690 1691

    最终图应如下所示:

![Figure 6.29: Scatter plot displaying predictions ](img/B15778_06_29.jpg)

图 6.29:显示预测的散点图

注意

W
wizardforcel 已提交
1692
要访问此特定部分的源代码,请参考[这里](https://packt.live/2BqDWvg)
W
wizardforcel 已提交
1693

W
wizardforcel 已提交
1694
您也可以通过[这里](https://packt.live/3ihPgKB)在线运行此示例。 您必须执行整个笔记本才能获得所需的结果。
W
wizardforcel 已提交
1695 1696 1697 1698 1699

## 活动 6.02:使用 LSTM 网络生成文本

### 解决方案

W
wizardforcel 已提交
1700
1.  导入所需的库,具体如下:
W
wizardforcel 已提交
1701

W
wizardforcel 已提交
1702 1703 1704 1705 1706 1707 1708 1709
    ```py
    import math
    import numpy as np
    import matplotlib.pyplot as plt
    import torch
    from torch import nn, optim
    import torch.nn.functional as F
    ```
W
wizardforcel 已提交
1710

W
wizardforcel 已提交
1711
2.  打开并将《爱丽丝梦游仙境》中的文字读入笔记本。打印前 50 个字符的摘要和文本文件的总长度。
W
wizardforcel 已提交
1712

W
wizardforcel 已提交
1713 1714 1715 1716 1717 1718
    ```py
    with open('alice.txt', 'r', encoding='latin1') as f:
        data = f.read()
    print("Extract: ", data[:50])
    print("Length: ", len(data))
    ```
W
wizardforcel 已提交
1719

W
wizardforcel 已提交
1720
3.  创建一个变量,其中包含数据集中未重复的字符列表。然后,创建一个字典,将每个字符映射到一个整数,其中字符将是键,整数将是值。
W
wizardforcel 已提交
1721

W
wizardforcel 已提交
1722 1723 1724 1725 1726
    ```py
    chars = list(set(data))
    indexer = {char: index for (index, char) in enumerate(chars)}
    The output should look as follows:
    ```
W
wizardforcel 已提交
1727 1728 1729

    输出应如下所示:

W
wizardforcel 已提交
1730 1731 1732 1733
    ```py
    Extract: ALICE was beginning to get very tired of sitting b
    Length: 145178
    ```
W
wizardforcel 已提交
1734

W
wizardforcel 已提交
1735
4.  将数据集的每个字母编码为其配对的整数。打印前 50 个编码字符和数据集编码版本的总长度。
W
wizardforcel 已提交
1736

W
wizardforcel 已提交
1737
    ```py
W
wizardforcel 已提交
1738
    indexed_data = []
W
wizardforcel 已提交
1739 1740 1741 1742 1743
    for c in data:
        indexed_data.append(indexer[c])
    print("Indexed extract: ", indexed_data[:50])
    print("Length: ", len(indexed_data))
    ```
W
wizardforcel 已提交
1744 1745 1746

    输出如下:

W
wizardforcel 已提交
1747 1748 1749 1750
    ```py
    Indexed extract: [51, 52, 29, 38, 28, 25, 11, 59, 39, 25, 16, 53, 2, 1, 26, 26, 1, 26, 2, 25, 56, 60, 25, 2, 53, 56, 25, 23, 53, 7, 45, 25, 56, 1, 7, 53, 13, 25, 60, 14, 25, 39, 1, 56, 56, 1, 26, 2, 25, 16]
    Length: 145178
    ```
W
wizardforcel 已提交
1751

W
wizardforcel 已提交
1752
5.  创建一个函数,接收一个批量,并将其编码为单热矩阵。
W
wizardforcel 已提交
1753

W
wizardforcel 已提交
1754 1755 1756 1757 1758 1759 1760 1761 1762 1763
    ```py
    def index2onehot(batch):
        batch_flatten = batch.flatten()
        onehot_flat = np.zeros((batch.shape[0] \
                                * batch.shape[1],len(indexer)))
        onehot_flat[range(len(batch_flatten)), batch_flatten] = 1
        onehot = onehot_flat.reshape((batch.shape[0], \
                                      batch.shape[1], -1))
        return onehot
    ```
W
wizardforcel 已提交
1764

W
wizardforcel 已提交
1765
    此函数采用二维矩阵并将其展平。 接下来,它创建一个平坦矩阵的形状和包含字母的字典长度的零填充矩阵(在“步骤 3”中创建)。 接下来,它用一个字符填充对应于批量中每个字符的字母。 最后,它对矩阵进行整形以使其为三维。
W
wizardforcel 已提交
1766

W
wizardforcel 已提交
1767
6.  创建一个定义网络架构的类。这个类应该包含一个额外的函数,用于初始化 LSTM 层的状态。
W
wizardforcel 已提交
1768

W
wizardforcel 已提交
1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793
    ```py
    class LSTM(nn.Module):
        def __init__(self, char_length, hidden_size, n_layers):
            super().__init__()
            self.hidden_size = hidden_size
            self.n_layers = n_layers
            self.lstm = nn.LSTM(char_length, hidden_size,\
                                n_layers, batch_first=True)
            self.output = nn.Linear(hidden_size, char_length)
        def forward(self, x, states):
            out, states = self.lstm(x, states)
            out = out.contiguous().view(-1, self.hidden_size)
            out = self.output(out)
            return out, states
        def init_states(self, batch_size):
            hidden = next(self.parameters())\
                          .data.new(self.n_layers, batch_size, \
                          self.hidden_size).zero_()
            cell = next(self.parameters())\
                   .data.new(self.n_layers,batch_size, \
                   self.hidden_size).zero_()
            states = (hidden, cell)
            return states
    This class contains an __init__ method where the
    ```
W
wizardforcel 已提交
1794

W
wizardforcel 已提交
1795
    此类包含`__init__`方法(其中定义了网络的架构),`forward`方法(用于确定通过层的数据流)以及`init_state`用零初始化隐藏状态和单元状态的方法。
W
wizardforcel 已提交
1796

W
wizardforcel 已提交
1797
7.  确定要从数据集中创建的批次数量,记住每个批次应该包含 100 个序列,每个序列的长度为 50 个。接下来,将编码后的数据分成 100 个序列。
W
wizardforcel 已提交
1798

W
wizardforcel 已提交
1799 1800
    ```py
    # Number of sequences per batch
W
wizardforcel 已提交
1801 1802
    n_seq = 100
    seq_length = 50
W
wizardforcel 已提交
1803 1804
    n_batches = math.floor(len(indexed_data) \
                / n_seq / seq_length)
W
wizardforcel 已提交
1805
    total_length = n_seq * seq_length * n_batches
W
wizardforcel 已提交
1806 1807 1808
    x = indexed_data[:total_length]
    x = np.array(x).reshape((n_seq,-1))
    ```
W
wizardforcel 已提交
1809

W
wizardforcel 已提交
1810
8.  通过使用`256`作为共两个循环层的隐藏单元数来实例化你的模型。
W
wizardforcel 已提交
1811

W
wizardforcel 已提交
1812 1813 1814 1815
    ```py
    model = LSTM(len(chars), 256, 2)
    model
    ```
W
wizardforcel 已提交
1816 1817 1818

    运行前面的代码将显示以下输出:

W
wizardforcel 已提交
1819 1820 1821 1822
    ```py
    LSTM(
      (lstm): LSTM(70, 256, num_layers=2, batch_first=True)
      (output): Linear(in_features=256, out_features=70, bias=True)
W
wizardforcel 已提交
1823
    )
W
wizardforcel 已提交
1824
    ```
W
wizardforcel 已提交
1825 1826 1827

    如果您的计算机有可用的 GPU,请确保使用以下代码片段将模型分配给 GPU:

W
wizardforcel 已提交
1828 1829 1830
    ```py
    model = LSTM(len(chars), 256, 2).to("cuda")
    ```
W
wizardforcel 已提交
1831

W
wizardforcel 已提交
1832
9.  定义损失函数和优化算法。使用 Adam 优化器和交叉熵损失来完成。训练网络`20`周期。
W
wizardforcel 已提交
1833

W
wizardforcel 已提交
1834 1835 1836 1837 1838
    ```py
    loss_function = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    epochs = 20
    ```
W
wizardforcel 已提交
1839

W
wizardforcel 已提交
1840
    如果您的机器有可用的 GPU,请尝试运行`500`周期的训练过程:
W
wizardforcel 已提交
1841

W
wizardforcel 已提交
1842 1843 1844
    ```py
    epochs = 500
    ```
W
wizardforcel 已提交
1845

W
wizardforcel 已提交
1846
0.  在每个周期,数据必须被划分为序列长度为 50 的批次。这意味着每个周期将有 100 个批次,每个批次的序列长度为 50。
W
wizardforcel 已提交
1847

W
wizardforcel 已提交
1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872
    ```py
    losses = []
    for e in range(1, epochs+1):
        states = model.init_states(n_seq)
        batch_loss = []
        for b in range(0, x.shape[1], seq_length):
            x_batch = x[:,b:b+seq_length]
            if b == x.shape[1] - seq_length:
                y_batch = x[:,b+1:b+seq_length]
                y_batch = np.hstack((y_batch, indexer["."] \
                          * np.ones((y_batch.shape[0],1))))
            else:
                y_batch = x[:,b+1:b+seq_length+1]
            x_onehot = torch.Tensor(index2onehot(x_batch))
            y = torch.Tensor(y_batch).view(n_seq * seq_length)
            pred, states = model(x_onehot, states)
            loss = loss_function(pred, y.long())
            optimizer.zero_grad()
            loss.backward(retain_graph=True)
            optimizer.step()
            batch_loss.append(loss.item())
        losses.append(np.mean(batch_loss))
        if e%2 == 0:
            print("epoch: ", e, "... Loss function: ", losses[-1])
    ```
W
wizardforcel 已提交
1873 1874 1875

    输出应如下所示:

W
wizardforcel 已提交
1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887
    ```py
    epoch: 2 ... Loss function: 3.1667490992052802
    epoch: 4 ... Loss function: 3.1473221943296235
    epoch: 6 ... Loss function: 2.897721455014985
    epoch: 8 ... Loss function: 2.567064647016854
    epoch: 10 ... Loss function: 2.4197753791151375
    epoch: 12 ... Loss function: 2.314083896834275
    epoch: 14 ... Loss function: 2.2241266349266313
    epoch: 16 ... Loss function: 2.1459227183769487
    epoch: 18 ... Loss function: 2.0731402758894295
    epoch: 20 ... Loss function: 2.0148646708192497
    ```
W
wizardforcel 已提交
1888 1889 1890

    如果您的计算机具有可用的 GPU,则用于训练网络的等效代码段如下所示:

W
wizardforcel 已提交
1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918
    ```py
    losses = []
    for e in range(1, epochs+1):
        states = model.init_states(n_seq)
        batch_loss = []
        for b in range(0, x.shape[1], seq_length):
            x_batch = x[:,b:b+seq_length]
            if b == x.shape[1] - seq_length:
                y_batch = x[:,b+1:b+seq_length]
                y_batch = np.hstack((y_batch, indexer["."] \
                                     * np.ones((y_batch.shape[0],1))))
            else:
                y_batch = x[:,b+1:b+seq_length+1]
            x_onehot = torch.Tensor(index2onehot(x_batch))\
                       .to("cuda")
            y = torch.Tensor(y_batch).view(n_seq * \
                                           seq_length).to("cuda")
            pred, states = model(x_onehot, states)
            loss = loss_function(pred, y.long())
            optimizer.zero_grad()
            loss.backward(retain_graph=True)
            optimizer.step()
            batch_loss.append(loss.item())
        losses.append(np.mean(batch_loss))
        if e%50 == 0:
            print("epoch: ", e, "... Loss function: ", \
                  losses[-1])
    ```
W
wizardforcel 已提交
1919

W
wizardforcel 已提交
1920
    将训练过程运行 500 个周期的结果如下:
W
wizardforcel 已提交
1921

W
wizardforcel 已提交
1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933
    ```py
    epoch: 50 ... Loss function: 1.5207843986050835
    epoch: 100 ... Loss function: 1.006190665836992
    epoch: 150 ... Loss function: 0.5197970939093622
    epoch: 200 ... Loss function: 0.24446514968214364
    epoch: 250 ... Loss function: 0.0640328845073437
    epoch: 300 ... Loss function: 0.007852113484565553
    epoch: 350 ... Loss function: 0.003644719101681278
    epoch: 400 ... Loss function: 0.006955199634078248
    epoch: 450 ... Loss function: 0.0030021724242973945
    epoch: 500 ... Loss function: 0.0034294885518992768
    ```
W
wizardforcel 已提交
1934 1935 1936

    可以看出,通过将训练过程运行更多的时间段,损失函数将达到较低的值。

W
wizardforcel 已提交
1937
1.  绘制损失随时间推移的进展情况。
W
wizardforcel 已提交
1938

W
wizardforcel 已提交
1939 1940 1941 1942 1943
    ```py
    x_range = range(len(losses))
    plt.plot(x_range, losses)
    plt.xlabel("epochs")
    plt.ylabel("Loss function")
W
wizardforcel 已提交
1944
    plt.show()
W
wizardforcel 已提交
1945
    ```
W
wizardforcel 已提交
1946 1947 1948 1949 1950 1951 1952

    该图表应如下所示:

    ![Figure 6.30: Chart displaying the progress of the loss function ](img/B15778_06_30.jpg)

    图 6.30:显示损失函数进度的图表

W
wizardforcel 已提交
1953
    如我们所见,在 20 个周期之后,损失函数仍然可以减少,这就是为什么强烈建议训练更多周期以便从模型中获得良好结果的原因。
W
wizardforcel 已提交
1954

W
wizardforcel 已提交
1955
2.  将下面的句子`starter`输入到训练好的模型中,让它来完成这个句子:`"So she was considering in her own mind "`
W
wizardforcel 已提交
1956

W
wizardforcel 已提交
1957 1958 1959 1960
    ```py
    starter = "So she was considering in her own mind "
    states = None
    ```
W
wizardforcel 已提交
1961 1962 1963

    如果您的计算机具有可用的 GPU,则将模型分配回 CPU 以执行预测:

W
wizardforcel 已提交
1964 1965 1966
    ```py
    model = model.to("cpu")
    ```
W
wizardforcel 已提交
1967

W
wizardforcel 已提交
1968
    首先,`for`循环的将种子输入模型,以便可以生成内存。 接下来,执行预测,如以下代码片段所示:
W
wizardforcel 已提交
1969

W
wizardforcel 已提交
1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990
    ```py
    for ch in starter:
        x = np.array([[indexer[ch]]])
        x = index2onehot(x)
        x = torch.Tensor(x)
        pred, states = model(x, states)
    counter = 0
    while starter[-1] != "." and counter < 100:
        counter += 1
        x = np.array([[indexer[starter[-1]]]])
        x = index2onehot(x)
        x = torch.Tensor(x)
        pred, states = model(x, states)
        pred = F.softmax(pred, dim=1)
        p, top = pred.topk(10)
        p = p.detach().numpy()[0]
        top = top.numpy()[0]
        index = np.random.choice(top, p=p/p.sum())
        starter += chars[index]
    print(starter)
    ```
W
wizardforcel 已提交
1991 1992 1993

    注意

W
wizardforcel 已提交
1994
    要访问此特定部分的源代码,请参考[这里](https://packt.live/2Bs6dRZ)。
W
wizardforcel 已提交
1995 1996 1997

    本部分当前没有在线交互示例,需要在本地运行。

W
wizardforcel 已提交
1998
    要访问此源代码的 GPU 版本,请参考[这里](https://packt.live/3g9X6UI)。 此版本的源代码无法作为在线交互示例使用,需要通过 GPU 设置在本地运行。
W
wizardforcel 已提交
1999

W
wizardforcel 已提交
2000
## 活动 6.03:用于情感分析的 NLP
W
wizardforcel 已提交
2001 2002 2003

### 解决方案

W
wizardforcel 已提交
2004
1.  导入所需的库。
W
wizardforcel 已提交
2005

W
wizardforcel 已提交
2006 2007 2008 2009 2010 2011 2012 2013 2014 2015
    ```py
    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    from string import punctuation
    from sklearn.metrics import accuracy_score
    import torch
    from torch import nn, optim
    import torch.nn.functional as F
    ```
W
wizardforcel 已提交
2016

W
wizardforcel 已提交
2017
2.  加载包含亚马逊 1,000 条产品评论的数据集,这些评论与`0`(负面评论)或`1`(正面评论)的标签配对。将数据分离成两个变量--一个包含评论,另一个包含标签。
W
wizardforcel 已提交
2018

W
wizardforcel 已提交
2019 2020 2021 2022 2023 2024
    ```py
    data = pd.read_csv("amazon_cells_labelled.txt", sep="\t", \
                       header=None)
    reviews = data.iloc[:,0].str.lower()
    sentiment = data.iloc[:,1].values
    ```
W
wizardforcel 已提交
2025

W
wizardforcel 已提交
2026
3.  去掉评论中的标点符号。
W
wizardforcel 已提交
2027

W
wizardforcel 已提交
2028 2029 2030 2031
    ```py
    for i in punctuation:
        reviews = reviews.str.replace(i,"")
    ```
W
wizardforcel 已提交
2032

W
wizardforcel 已提交
2033
4.  创建一个变量,包含整个评论集的词汇量。此外,创建一个字典,将每个单词映射到一个整数,其中单词将是键,整数将是值。
W
wizardforcel 已提交
2034

W
wizardforcel 已提交
2035 2036 2037 2038 2039 2040 2041
    ```py
    words = ' '.join(reviews)
    words = words.split()
    vocabulary = set(words)
    indexer = {word: index for (index, word) \
               in enumerate(vocabulary)}
    ```
W
wizardforcel 已提交
2042

W
wizardforcel 已提交
2043
5.  通过将评论中的每个词替换为其配对的整数来对评论数据进行编码。
W
wizardforcel 已提交
2044

W
wizardforcel 已提交
2045
    ```py
W
wizardforcel 已提交
2046
    indexed_reviews = []
W
wizardforcel 已提交
2047 2048 2049 2050
    for review in reviews:
        indexed_reviews.append([indexer[word] \
                                for word in review.split()])
    ```
W
wizardforcel 已提交
2051

W
wizardforcel 已提交
2052
6.  创建一个包含网络架构的类。确保你包含一个嵌入层。
W
wizardforcel 已提交
2053

W
wizardforcel 已提交
2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072
    ```py
    class LSTM(nn.Module):
        def __init__(self, vocab_size, embed_dim, \
                     hidden_size, n_layers):
            super().__init__()
            self.hidden_size = hidden_size
            self.embedding = nn.Embedding(vocab_size, embed_dim)
            self.lstm = nn.LSTM(embed_dim, hidden_size, \
                                n_layers, batch_first=True)
            self.output = nn.Linear(hidden_size, 1)
        def forward(self, x):
            out = self.embedding(x)
            out, _ = self.lstm(out)
            out = out.contiguous().view(-1, self.hidden_size)
            out = self.output(out)
            out = out[-1,0]
            out = torch.sigmoid(out).unsqueeze(0)
            return out
    ```
W
wizardforcel 已提交
2073 2074 2075

    返回

W
wizardforcel 已提交
2076
    该类包含用于定义网络架构的`__init__`方法和用于确定数据流经不同层的方式的`forward`方法。
W
wizardforcel 已提交
2077

W
wizardforcel 已提交
2078
7.  使用 64 个嵌入维度和 128 个神经元为三个 LSTM 层实例化模型。
W
wizardforcel 已提交
2079

W
wizardforcel 已提交
2080 2081 2082 2083
    ```py
    model = LSTM(len(vocabulary), 64, 128, 3)
    model
    ```
W
wizardforcel 已提交
2084 2085 2086

    运行前面的代码将显示以下输出:

W
wizardforcel 已提交
2087 2088 2089 2090 2091
    ```py
    LSTM(
      (embedding): Embedding(1905, 64)
      (lstm): LSTM(64, 128, num_layers=3, batch_first=True)
      (output): Linear(in_features=128, out_features=1, bias=True)
W
wizardforcel 已提交
2092
    )
W
wizardforcel 已提交
2093
    ```
W
wizardforcel 已提交
2094 2095


W
wizardforcel 已提交
2096
8.  定义损失函数,优化算法,以及训练的周期数。例如,您可以使用二进制交叉熵损失作为损失函数,Adam 优化器,并训练 10 个周期。
W
wizardforcel 已提交
2097

W
wizardforcel 已提交
2098 2099 2100 2101 2102
    ```py
    loss_function = nn.BCELoss()
    optimizer = optim.Adam(model.parameters(), lr=0.001)
    epochs = 10
    ```
W
wizardforcel 已提交
2103

W
wizardforcel 已提交
2104
9.  创建一个`for`循环,通过不同的周期,并分别通过每一个单次评论。对于每一个评论,进行预测,计算损失函数,并更新网络的参数。此外,计算该训练数据上网络的准确率。
W
wizardforcel 已提交
2105

W
wizardforcel 已提交
2106 2107
    ```py
    losses = []
W
wizardforcel 已提交
2108
    acc = []
W
wizardforcel 已提交
2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133
    for e in range(1, epochs+1):
        single_loss = []
        preds = []
        targets = []
        for i, r in enumerate(indexed_reviews):
            if len(r) <= 1:
                continue
            x = torch.Tensor([r]).long()
            y = torch.Tensor([sentiment[i]])
            pred = model(x)
            loss = loss_function(pred, y)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            final_pred = np.round(pred.detach().numpy())
            preds.append(final_pred)
            targets.append(y)
            single_loss.append(loss.item())
        losses.append(np.mean(single_loss))
        accuracy = accuracy_score(targets,preds)
        acc.append(accuracy)
        if e%1 == 0:
            print("Epoch: ", e, "... Loss function: ", losses[-1], \
                  "... Accuracy: ", acc[-1])
    ```
W
wizardforcel 已提交
2134

W
wizardforcel 已提交
2135
    与以前的活动一样,训练过程包括进行预测,将其与基本事实进行比较以计算损失函数,并执行反向传播以最小化损失函数。
W
wizardforcel 已提交
2136

W
wizardforcel 已提交
2137
0.  绘制损失和精度随时间的进展情况。以下代码用于绘制损失函数。
W
wizardforcel 已提交
2138

W
wizardforcel 已提交
2139 2140 2141 2142 2143
    ```py
    x_range = range(len(losses))
    plt.plot(x_range, losses)
    plt.xlabel("epochs")
    plt.ylabel("Loss function")
W
wizardforcel 已提交
2144
    plt.show()
W
wizardforcel 已提交
2145
    ```
W
wizardforcel 已提交
2146 2147 2148

    该图应如下所示:

W
wizardforcel 已提交
2149
    ![Figure 6.31: Plot displaying the progress of the loss function  ](img/B15778_06_31.jpg)
W
wizardforcel 已提交
2150

W
wizardforcel 已提交
2151
    图 6.31:显示损失函数进度的图
W
wizardforcel 已提交
2152

W
wizardforcel 已提交
2153
    以下代码用于绘制准确率得分:
W
wizardforcel 已提交
2154

W
wizardforcel 已提交
2155 2156 2157 2158 2159 2160 2161
    ```py
    x_range = range(len(acc))
    plt.plot(x_range, acc)
    plt.xlabel("epochs")
    plt.ylabel("Accuracy score")
    plt.show()
    ```
W
wizardforcel 已提交
2162

W
wizardforcel 已提交
2163
    该图应如下所示:
W
wizardforcel 已提交
2164

W
wizardforcel 已提交
2165
    ![Figure 6.32: Plot displaying the progress of the accuracy score ](img/B15778_06_32.jpg)
W
wizardforcel 已提交
2166

W
wizardforcel 已提交
2167
    图 6.32:显示准确率得分进度的图
W
wizardforcel 已提交
2168

W
wizardforcel 已提交
2169
    注意
W
wizardforcel 已提交
2170

W
wizardforcel 已提交
2171
    要访问此特定部分的源代码,请参考[这里](https://packt.live/2VyX0ON)。
W
wizardforcel 已提交
2172

W
wizardforcel 已提交
2173
    本部分当前没有在线交互示例,需要在本地运行。