gitbook/PyTorch深度学习实战/docs/483203.md
2022-09-03 22:05:03 +08:00

351 lines
14 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 答疑篇|思考题答案集锦
你好,我是编辑宇新。春节将至,给你拜个早年。
距离我们的专栏更新结束,已经过去不少时间啦。方远老师仍然会在工作之余,回到专栏里转一转,看看同学最新的学习动态。大部分的疑问,老师都在留言区里做了回复。
除了紧跟更新的第一批同学,也很开心看到有更多新朋友加入到这个专栏的学习中。课程的思考题,为了给你留足思考和研究的时间,我们选择用加餐的方式,把所有参考答案一次性发布出来。
这里要提醒一下,建议你先自己思考和练习后,再来对答案。每节课都有超链接,方便你跳转回顾。
## [第2节课](https://time.geekbang.org/column/article/426126)
题目:在刚才用户对游戏评分的那个问题中,你能计算一下每位用户对三款游戏打分的平均分吗?
答案:
```plain
>>>interest_score.mean(axis=1)
```
## [第3节课](https://time.geekbang.org/column/article/42680)
题目给定数组scores形状为2562562scores\[: , :, 0\] 与scores\[:, :, 1\]对应位置元素的和为1现在我们要根据scores生产数组mask要求scores通道0的值如果大于通道1的值则mask对应的位置为0否则为1。
scores如下你可以试试用代码实现
```plain
scores = np.random.rand(256, 256, 2)
scores[:,:,1] = 1 - scores[:,:,0]
```
答案:
```plain
mask = np.argmax(scores, axis=2)
```
## [第4节课](https://time.geekbang.org/column/article/427460)
题目在PyTorch中有torch.Tensor()和torch.tensor()两种函数,它们的区别是什么呢?
答案torch.Tensor()**是Pytorch中的类**其实它是torch.FloatTensor()的别名使用torch.Tensor()会调用Tensor类的构造函数生成float类型的张量
而torch.tensor()**是Pytorch的函数**函数原型是torch.tensor(data, dtype…)其中data可以是scalarlisttuple等不同的数据结构形式。
## [第5节课](https://time.geekbang.org/column/article/428186)
题目现在有个Tensor如下。
```python
>>> A=torch.tensor([[4,5,7], [3,9,8],[2,3,4]])
>>> A
tensor([[4, 5, 7],
        [3, 9, 8],
        [2, 3, 4]])
```
我们想提取出其中第一行的第一个,第二行的第一第二个,第三行的最后一个,该怎么做呢?
答案:
```python
>>> B=torch.Tensor([[1,0,0], [1,1,0],[0,0,1]]).type(torch.ByteTensor)
>>> B
tensor([[1, 0, 0],
        [1, 1, 0],
        [0, 0, 1]], dtype=torch.uint8)
>>> C=torch.masked_select(A,B)
>>> C
tensor([4, 3, 9, 4])
```
我们只需要创建一个形状跟A一样的Tensor然后将对应位置的数值置为1然后再把Tensor转换成torch.ByteTensor类型得到B最后跟之前masked\_select一样的操作就OK啦。
## [第6节课](https://time.geekbang.org/column/article/429048)
题目在PyTorch中我们要定义一个数据集应该继承哪一个类呢
答案torch.utils.data.Dataset
## [第7节课](https://time.geekbang.org/column/article/429826)
题目Torchvision中 transforms 模块的作用是什么?
答案常用的图像操作例如随机切割、旋转、Tensor 与 Numpy 和 PIL Image 的数据类型转换等。
## [第8节课](https://time.geekbang.org/column/article/431420)
题目:请你使用`torchvision.models`模块实例化一个VGG 16网络。
答案:
```python
import torchvision.models as models
vgg16 = models.vgg16(pretrained=True)
```
## [第9节课](https://time.geekbang.org/column/article/432042)
题目请你想一想padding为samestride可以为1以外的数值吗
答案:不可以。
## [第10节课](https://time.geekbang.org/column/article/433801)
题目随机生成一个3通道的128x128的特征图然后创建一个有10个卷积核且卷积核尺寸为3x3DW卷积的深度可分离卷积对输入数据进行卷积计算。
答案:
```plain
import torch
import torch.nn as nn
# 生成一个三通道的128x128特征图
x = torch.rand((3, 128, 128)).unsqueeze(0)
# DW卷积groups参数与输入通道数一样
dw = nn.Conv2d(x.shape[1], x.shape[1], 3, 1, groups=x.shape[1])
pw = nn.Conv2d(x.shape[1], 10, 1, 1)
out = pw(dw(x))
print(out.shape)
```
## [第11节课](https://time.geekbang.org/column/article/435553)
题目:损失函数的值越小越好么?
答案:不是的,咱们在这节课中学习的损失函数,实际上是模型在训练数据上的平均损失,这种损失函数我们称作为经验风险。实际上,还有一个方面也是我们在实际工作中需要考虑的,那就是模型的复杂度:一味追求经验风险的最小化,很容易使得模型过拟合(可回顾一下前文内容)。
所以,还需要对模型的复杂度进行约束,我们称之为结构风险。实际研发场景中**,最终的损失函数是由经验风险和结构风险共同组成的,我们要求的是两者之和的最小化**。
## [第12节课](https://time.geekbang.org/column/article/436564)
题目:深度学习都是基于反向传播的么?
答案:不是的,主流的深度学习模型是基于反向传播和梯度下降的,但是一些非梯度下降的二阶优化算法也是存在的,比如拟牛顿法等。不过计算代价非常大,用的就比较少了。而且一般而言,工业界基本都采用基于反向传播和梯度下降的方式。
## [第13节课](https://time.geekbang.org/column/article/438639)
题目batch size越大越好吗
答案不是的。较大的batch\_size容易使模型收敛在局部最优点特别小则容易受噪声影响。
## [第14节课](https://time.geekbang.org/column/article/442442)
题目请你自己构建一个卷积神经网络基于CIFAR-10训练一个图像分类模型。因为还没有学习图像分类原理所以我先帮你写好了网络的结构需要你补全数据读取、损失函数(交叉熵损失)与优化方法SGD等部分。
```python
class MyCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3)
        # conv1输出的特征图为222x222大小
        self.fc = nn.Linear(16 * 222 * 222, 10)
    def forward(self, input):
        x = self.conv1(input)
        # 进去全连接层之前,先将特征图铺平
        x = x.view(x.shape[0], -1)
        x = self.fc(x)
        return x
```
答案:
```python
import torch
import torch.nn as nn
import torchvision
import torchvision.transforms as transforms
from torch.utils.data import DataLoader
transform = transforms.Compose([
    transforms.RandomResizedCrop((224,224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
    ])
cifar10_dataset = torchvision.datasets.CIFAR10(root='./data',
                                       train=False,
                                       transform=transform,
                                       target_transform=None,
                                       download=True)
dataloader = DataLoader(dataset=cifar10_dataset, # 传入的数据集, 必须参数
                               batch_size=32,       # 输出的batch大小
                               shuffle=True,       # 数据是否打乱
                               num_workers=2)      # 进程数, 0表示只有主进程
class MyCNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(3, 16, kernel_size=3)
        # conv1输出的特征图为222x222大小
        self.fc = nn.Linear(16 * 222 * 222, 10)
    def forward(self, input):
        x = self.conv1(input)
        # 进去全连接层之前,先将特征图铺平
        x = x.view(x.shape[0], -1)
        x = self.fc(x)
        return x
    
cnn = MyCNN()
optimizer = torch.optim.SGD(cnn.parameters(), lr=1e-5, weight_decay=1e-2, momentum=0.9)
# 训练3个Epoch
for epoch in range(3):
    for step, (images, target) in enumerate(dataloader):
        output = cnn(images)
        loss = nn.CrossEntropyLoss()(output, target)
        print('Epoch: {} Step: {} Loss: {}'.format(epoch + 1 , step, loss))
        cnn.zero_grad()
        loss.backward()
        optimizer.step()
```
## [第15节课](https://time.geekbang.org/column/article/444252)
题目参考Visdom快速上手中的例子现在需要生成两组随机数分别表示Loss和Accuracy。在迭代的过程中如何用代码同时绘制出Loss和Accuracy两组数据呢
答案:
```python
from visdom import Visdom
import numpy as np
import time
# 实例化窗口
viz = Visdom(port=6006) 
# 初始化窗口参数
viz.line([[0.,0.]], [0.],
win='train',
opts=dict(title='loss&acc', legend=['loss','acc'])
)
for step in range(10):
    loss = 0.2 * np.random.randn() + 1
    acc = 0.1 * np.random.randn() + 0.5
    # 更新窗口数据
    viz.line([[loss, acc]], [step], win='train', update='append')
    time.sleep(0.5)
```
运行结果如图所示:
![图片](https://static001.geekbang.org/resource/image/7e/18/7eecfc47a74d6fa319d45eb6092dec18.jpg?wh=1920x1075)
## [第16节课](https://time.geekbang.org/column/article/445886)
题目在torch.distributed.init\_process\_group(backend=“nccl”)函数中backend参数可选哪些后端它们分别有什么区别
答案backend参数指定的通信后端包括NCCL、MPI、gloo。NCCL是Nvidia提供的官方多卡通信框架相对比较高效MPI也是高性能计算常用的通信协议但是需要自己安装MPI实现框架例如OpenMPIgloo是内置通信后端但不够高效。
## [第18节课](https://time.geekbang.org/column/article/447503)
题目:老板希望你的模型能尽可能的把线上所有极客时间的海报都找到,允许一些误召回。训练模型的时候你应该侧重精确率还是召回率?
答案:侧重召回率。
## [第19节课](https://time.geekbang.org/column/article/450898)
题目对于这节课里讲的小猫分割问题最终只输出1个特征图是否可以
答案可以的因为小猫分割是一个二分类问题可以将输出的特征图使用sigmoid函数将输出的数值转换为一个概率从而进行判断。
## [第20节课](https://time.geekbang.org/column/article/455415)
题目:图像分割的评价指标都有什么?
答案mIoU和Dice系数。
## [第21节课](https://time.geekbang.org/column/article/460504)
题目TF-IDF有哪些缺点呢你不妨结合它的计算过程做个梳理。
答案TF-IDF认为文本频率小的单词就越重要也就是区分性越强但是实际上很多情况下这并不正确。比如一篇财经类文章有一句“股价就跟火箭一样上了天”这里的“火箭”就会变得非常重要显然是错误的。怎么办呢一般我们会对词频做一个条件过滤比如超过多少次。也会对TF-IDF的公式进行改进具体改进方法如果有兴趣的话你可以借助网络查找相应的文章。
## [第22节课](https://time.geekbang.org/column/article/461691)
题目:词向量的长度多少比较合适呢?越长越好吗?
答案不是的越长的词向量尽管可以更加精细的表示词语的空间位置但是也会带来计算量的暴涨、数据稀疏等问题一般来说我们较多的选择64、128、256这样的长度具体是多少要靠实验来不断的确定。有的论文给出的建议是n>8.33logN,具体是否可行,还是要结合实际情况来敲定。
## [第23节课](https://time.geekbang.org/column/article/462524)
题目利用今天训练的模型编写一个函数predict\_sentiment实现输入一句话输出这句话的情绪类别与概率。
例如:
```python
text = "This film is terrible!"
predict_sentiment(text, model, tokenizer, vocab, device)
'''
输出:('neg', 0.8874172568321228)
'''
```
答案:参考代码如下。
```python
# 预测过程
def predict_sentiment(text, model, tokenizer, vocab, device):
    tokens = tokenizer(text)
    ids = [vocab[t] for t in tokens]
    length = torch.LongTensor([len(ids)])
    tensor = torch.LongTensor(ids).unsqueeze(dim=0).to(device)
    prediction = model(tensor, length).squeeze(dim=0)
    probability = torch.softmax(prediction, dim=-1)
    predicted_class = prediction.argmax(dim=-1).item()
    predicted_probability = probability[predicted_class].item()
    predicted_class = 'neg' if predicted_class == 0 else 'pos'
    return predicted_class, predicted_probability
# 加载模型
model.load_state_dict(torch.load('lstm.pt'))
text = "This film is terrible!"
predict_sentiment(text, model, tokenizer, vocab, device)
```
## [第24节课](https://time.geekbang.org/column/article/464152)
题目Bert处理文本是有最大长度要求的512那么遇到长文本该怎么办呢
答案这是一个非常开放的问题设置为最大512主要还是兼顾了效率问题但还是有非常多的解决办法比如我们之前提到过的关键词提取。或者分别从开头、中间、结尾选择一定长度的内容做运算。不过这些都是比较简单的办法。你还有更好的办法吗欢迎留言给我。
## [第25节课](https://time.geekbang.org/column/article/464870?)
题目自2018年BERT被提出以来获得了很大的成功学术界陆续提出了各类相关模型例如我们今天学习的BART。请你查一查还有哪些BERT系列的模型并阅读相关论文自行学习一下它们的原理与特点。
答案:
[XLNet: Generalized Autoregressive Pretraining for Language Understanding](https://arxiv.org/abs/1906.08237)
[RoBERTa: A Robustly Optimized BERT Pretraining Approach](https://arxiv.org/abs/1907.11692)
[ALBERT: A Lite BERT for Self-supervised Learning of Language Representations](https://arxiv.org/abs/1909.11942)
最后,再次祝愿你虎年快乐,学习进步,工作顺利!