351 lines
14 KiB
Markdown
351 lines
14 KiB
Markdown
|
# 答疑篇|思考题答案集锦
|
|||
|
|
|||
|
你好,我是编辑宇新。春节将至,给你拜个早年。
|
|||
|
|
|||
|
距离我们的专栏更新结束,已经过去不少时间啦。方远老师仍然会在工作之余,回到专栏里转一转,看看同学最新的学习动态。大部分的疑问,老师都在留言区里做了回复。
|
|||
|
|
|||
|
除了紧跟更新的第一批同学,也很开心看到有更多新朋友加入到这个专栏的学习中。课程的思考题,为了给你留足思考和研究的时间,我们选择用加餐的方式,把所有参考答案一次性发布出来。
|
|||
|
|
|||
|
这里要提醒一下,建议你先自己思考和练习后,再来对答案。每节课都有超链接,方便你跳转回顾。
|
|||
|
|
|||
|
## [第2节课](https://time.geekbang.org/column/article/426126)
|
|||
|
|
|||
|
题目:在刚才用户对游戏评分的那个问题中,你能计算一下每位用户对三款游戏打分的平均分吗?
|
|||
|
|
|||
|
答案:
|
|||
|
|
|||
|
```plain
|
|||
|
>>>interest_score.mean(axis=1)
|
|||
|
|
|||
|
```
|
|||
|
|
|||
|
## [第3节课](https://time.geekbang.org/column/article/42680)
|
|||
|
|
|||
|
题目:给定数组scores,形状为(256,256,2),scores\[: , :, 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可以是scalar,list,tuple等不同的数据结构形式。
|
|||
|
|
|||
|
## [第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为’same’时,stride可以为1以外的数值吗?
|
|||
|
|
|||
|
答案:不可以。
|
|||
|
|
|||
|
## [第10节课](https://time.geekbang.org/column/article/433801)
|
|||
|
|
|||
|
题目:随机生成一个3通道的128x128的特征图,然后创建一个有10个卷积核且卷积核尺寸为3x3(DW卷积)的深度可分离卷积,对输入数据进行卷积计算。
|
|||
|
|
|||
|
答案:
|
|||
|
|
|||
|
```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实现框架,例如OpenMPI;gloo是内置通信后端,但不够高效。
|
|||
|
|
|||
|
## [第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)
|
|||
|
|
|||
|
最后,再次祝愿你虎年快乐,学习进步,工作顺利!
|
|||
|
|