引言

  刚进入人工智能实验室,不知道是在学习机器学习还是深度学习,想来他俩可能是一个东西,查阅之后才知道这是两个领域,或许也有些交叉,毕竟我也刚接触,不甚了解。

  在我还是个纯度小白之时,写下这篇文章,希望后来同现在的我一样,刚刚涉足此领域的同学能够在这,跨越时空,在小白与小白的交流中得到些许帮助。

开始

  在只会一些python语法,其他啥都没有,第一周老师讲了一些机器学习和深度学习的了解性内容,就给了一个实验,让我们一周内弄懂并跑出来,其实老师的代码已经完成了,我们可以直接放进Pycharm里跑出来,但是代码细节并没有讲,俗话说师傅领进门,修行在个人。那就从最基本的开始,把这个代码弄懂,把实验理解。

  下面我会先把整个代码贴出来,之后一步一步去分析每个模块,每个函数的作用。

说明

  本实验来自此处博客,我们的实验也是基于这个博客的内容学习的。

一、数据集

数据源于kaggle,可在此链接自行下载

二、运行代码

三、解析

从头开始,先来看这段代码(注释进行解释):

# 进行一系列数据增强,然后生成训练(train)、验证(val)、和测试(test)数据集
data_transforms = {
'train': transforms.Compose([
transforms.RandomResizedCrop(input_size),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
'val': transforms.Compose([
transforms.Resize(input_size),
transforms.CenterCrop(input_size),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
'test': transforms.Compose([
transforms.Resize(input_size),
transforms.CenterCrop(input_size),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])
}

这是一个字典数据类型,其中键分别为:train、val、test对应数据集里的三个文件夹train、val、test

键train对应的值是一个操作transfroms.Compose( [列表] )

参数为一个列表,列表中的元素为四个操作:

transforms.RandomResizedCrop()

transforms.RandomHorizontalFlip()

transforms.ToTensor

transforms.ToTensortransforms.Normalize()

transforms在torchvision中,一个图像处理包,可以通过它调用一些图像处理函数,对图像进行处理

transfroms.Compose( [列表] ):此函数存在于torchvision.transforms中,一般用Compose函数把多个步骤整合到一起

transforms.RandomResizedCrop(数字):将给定图像随机裁剪为不同的大小和宽高比,然后缩放所裁剪得到的图像为制定的大小;(即先随机采集,然后对裁剪得到的图像缩放为同一大小)

例如:

transforms.RandomHorizontalFlip():以给定的概率随机水平旋转给定的PIL的图像,默认为0.5;

例如:

transforms.ToTensor:将给定图像转为Tensor(一个数据类型,类似有深度的矩阵)

例如:

transforms.ToTensortransforms.Normalize():归一化处理

例如:

函数详细内容请查看此处文章

可以看出,这一块的代码就是定义一种操作集合,将一张图片进行剪裁、旋转、转为一种数据、数据归一化

继续往下看,相应的解释都在注释中

image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x]) for x in
['train', 'val', 'test']}
dataloaders_dict = {
x: torch.utils.data.DataLoader(image_datasets[x], batch_size=batch_size, shuffle=True, num_workers=0) for x in
['train', 'val', 'test']}

首先是数据导入部分,这里采用官方写好的torchvision.datasets.ImageFolder接口实现数据导入。这个接口需要你提供图像所在的文件夹

x是字典的键,从后面的for迭代的范围中获取,有'train', 'val', 'test'三个值

datasets.ImageFolder(os.path.join(data_dir, x), data_transforms[x])高亮部分是数据集的文件夹路径,找到文件后,确定x,在执行第二个参数data_transforms[x],把x进行一些列处理

前面torchvision.datasets.ImageFolder只是返回列表,列表是不能作为模型输入的(我也不知道为什么),因此在PyTorch中需要用另一个类来封装列表,那就是:torch.utils.data.DataLoader

torch.utils.data.DataLoader类可以将列表类型的输入数据封装成Tensor数据格式,以备模型使用。

好,我们继续往下看

# 定义一个查看图片和标签的函数
def imshow(inp, title=None):
# transpose(0,1,2),0是x轴,1是y轴,2是z轴,由(0,1,2)变为(1,2,0)就是x和z轴先交换,x和y轴再交换
inp = inp.numpy().transpose((1, 2, 0))
mean = np.array([0.485, 0.456, 0.406]) # 创建一个数组[0.485, 0.456, 0.406]
std = np.array([0.229, 0.224, 0.225]) # 同样也是创建一个数组
inp = std * inp + mean # 调整图像尺寸大小等
inp = np.clip(inp, 0, 1) # 小于0的都为0,大于1的都为1,之间的不变
plt.imshow(inp) # 设置图像为灰色
if title is not None: # 如果图像有标题则显示标题
plt.title(title) # 设置图像标题
plt.pause(0.001) # 窗口绘制后停留0.001秒 imgs, labels = next(iter(dataloaders_dict['train'])) # 自动往下迭代参数对象
out = torchvision.utils.make_grid(imgs[:8]) # 将8个图拼成一张图片
classes = image_datasets['test'].classes # 每个图像的文件名
# out是一个8个图片拼成的长图,经过imshow()处理后附加标题(图片文件名的前8个字母)输出
# imshow(out, title=[classes[x] for x in labels[:8]])

输出后,在IDE中是这样的(右上角):

好,想在继续往下走

下面呢给出了四个训练模型,实战中我们只需要挑其中一个进行训练就好,其他的模型要注释掉,下面代码上四个模型我都会分析

  

# inception------------------------------------------------------inception模型,有趣的是它可以翻译为盗梦空间
model = models.inception_v3(pretrained=True)
# inception_v3是一个预训练模型, pretrained=True执行后会把模型下载到我们的电脑上
model.aux_logits = False # 是否给模型创建辅助,具体增么个辅助太复杂,请观众老爷们自行谷歌
num_fc_in = model.fc.in_features # 提取fc层固定的参数
# 改变全连接层,2分类问题,out_features = 2
model.fc = nn.Linear(num_fc_in, num_classes) # 修改fc层参数为num_classes = 4(最前面前面定义了) # alexnet--------------------------------------------------------alexnet模型
model = models.alexnet(pretrained=True) # alexnet是一个预训练模型, pretrained=True执行后会把模型下载到我们的电脑上
num_fc_in = model.classifier[6].in_features # 提取fc层固定的参数
model.fc = torch.nn.Linear(num_fc_in, num_classes) # 修改fc层参数为num_classes = 4(最前面前面定义了)
model.classifier[6] = model.fc
#将图层初始化为model.fc
#相当于model.classifier[6] = torch.nn.Linear(num_fc_in, num_classes) # 建立VGG16迁移学习模型------------------------------------------------vgg16模型
model = torchvision.models.vgg16(pretrained=True)# vgg16是一个预训练模型, pretrained=True执行后会把模型下载到我们的电脑上
# 先将模型参数改为不可更新
for param in model.parameters():
param.requires_grad = False
# 再更改最后一层的输出,至此网络只能更改该层参数
model.classifier[6] = nn.Linear(4096, num_classes)
model.classifier = torch.nn.Sequential( # 修改全连接层 自动梯度会恢复为默认值
torch.nn.Linear(25088, 4096),
torch.nn.ReLU(),
torch.nn.Dropout(p=0.5),
torch.nn.Linear(4096, 4096),
torch.nn.Dropout(p=0.5),
torch.nn.Linear(4096, num_classes)) # resnet18---------------------------------------------------------------resnet模型(和前几个模型差不多,自己脑部吧)
model = models.resnet18(pretrained=True)
# 全连接层的输入通道in_channels个数
num_fc_in = model.fc.in_features
# 改变全连接层,2分类问题,out_features = 2
model.fc = nn.Linear(num_fc_in, num_classes)

继续,解释都在注释里了

# 定义训练函数
def train_model(model, dataloaders, criterion, optimizer, mundde_epochs=25):
since = time.time() # 返回当前时间的时间戳(1970纪元后经过的浮点秒数)
# state_dict变量存放训练过程中需要学习的权重和偏执系数,state_dict作为python的字典对象将每一层的参数映射成tensor张量,
# 需要注意的是torch.nn.Module模块中的state_dict只包含卷积层和全连接层的参数
best_model_wts = copy.deepcopy(model.state_dict()) # copy是一个复制函数
best_acc = 0.0
# 下面这个迭代就是一个进度条的输出,从0到9显示进度
for epoch in range(num_epochs):
print('Epoch {}/{}'.format(epoch, num_epochs - 1))
print('-' * 10)
# 下面这个迭代,范围就两个'train', 'val',对应不执行不同的训练模式
for phase in ['train', 'val']: if phase == 'train':
model.train()
else:
model.eval() running_loss = 0.0
running_corrects = 0.0 for inputs, labels in dataloaders[phase]:
# 下面这行代码的意思是将所有最开始读取数据时的tensor变量copy一份到device所指定的GPU或CPU上去,
# 之后的运算都在GPU或CPU上进行
inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() # 模型梯度设为0 # 接下来所有的tensor运算产生的新的节点都是不可求导的
with torch.set_grad_enabled(phase == 'train'):
outputs = model(inputs) # output等于把inputs放到指定设备上去运算
loss = criterion(outputs, labels) # loss为outputs和labels的交叉熵损失
# 举例:output = torch.max(input, dim)
# 输入
# input是softmax函数输出的一个tensor
# dim是max函数索引的维度0 / 1,0是每列的最大值,1是每行的最大值
# 输出
# 函数会返回两个tensor,第一个tensor是每行的最大值,softmax的输出中最大的是1,所以第一个tensor是全1的tensor;
# 第二个tensor是每行最大值的索引。
_, preds = torch.max(outputs, 1)
if phase == 'train':
loss.backward() # 反向传播计算得到每个参数的梯度值
optimizer.step() # 通过梯度下降执行一步参数更新 running_loss += loss.item() * inputs.size(0)
running_corrects += (preds == labels).sum().item()
epoch_loss = running_loss / len(dataloaders[phase].dataset)
epoch_acc = running_corrects / len(dataloaders[phase].dataset) print('{} loss: {:.4f} Acc: {:.4f}'.format(phase, epoch_loss, epoch_acc)) if phase == 'val' and epoch_acc > best_acc:
best_acc = epoch_acc
best_model_wts = copy.deepcopy(model.state_dict())
print()
time_elapsed = time.time() - since
print('Training complete in {:.0f}m {:.0f}s'.format(time_elapsed // 60, time_elapsed % 60))
print('Best val Acc: {:.4f}'.format(best_acc)) model.load_state_dict(best_model_wts)
return model

继续往下看

# 定义优化器和损失函数
model = model.to(device) # 前面解释过了
optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
# optimizer = optim.Adam(model.classifier.parameters(), lr=0.0001) # sched = optim.lr_scheduler.StepLR(optimizer, step_size=4, gamma=0.1)
criterion = nn.CrossEntropyLoss() # 交叉熵损失函数

引用此文章

class torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)[source]

实现随机梯度下降算法(momentum可选)。

参数:

params (iterable) – 待优化参数的iterable或者是定义了参数组的dict

lr (float) – 学习率

momentum (float, 可选) – 动量因子(默认:0)

weight_decay (float, 可选) – 权重衰减(L2惩罚)(默认:0)

dampening (float, 可选) – 动量的抑制因子(默认:0)

nesterov (bool, 可选) – 使用Nesterov动量(默认:False)

例子:

optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
optimizer.zero_grad()
loss_fn(model(input), target).backward()
optimizer.step()

class torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)

adam算法来源:Adam: A Method for Stochastic Optimization

Adam(Adaptive Moment Estimation)本质上是带有动量项的RMSprop,它利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。它的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。

其公式如下:



参数:

params(iterable):可用于迭代优化的参数或者定义参数组的dicts。
lr (float, optional) :学习率(默认: 1e-3)
betas (Tuple[float, float], optional):用于计算梯度的平均和平方的系数(默认: (0.9, 0.999))
eps (float, optional):为了提高数值稳定性而添加到分母的一个项(默认: 1e-8)
weight_decay (float, optional):权重衰减(如L2惩罚)(默认: 0)
step(closure=None)函数:执行单一的优化步骤
closure (callable, optional):用于重新评估模型并返回损失的一个闭包

图像分类学习:X光胸片诊断识别----迁移学习的更多相关文章

  1. 深度学习趣谈:什么是迁移学习?(附带Tensorflow代码实现)

    一.迁移学习的概念 什么是迁移学习呢?迁移学习可以由下面的这张图来表示: 这张图最左边表示了迁移学习也就是把已经训练好的模型和权重直接纳入到新的数据集当中进行训练,但是我们只改变之前模型的分类器(全连 ...

  2. 图像识别 | AI在医学上的应用 | 深度学习 | 迁移学习

    参考:登上<Cell>封面的AI医疗影像诊断系统:机器之心专访UCSD张康教授 Identifying Medical Diagnoses and Treatable Diseases b ...

  3. 基于双向LSTM和迁移学习的seq2seq核心实体识别

    http://spaces.ac.cn/archives/3942/ 暑假期间做了一下百度和西安交大联合举办的核心实体识别竞赛,最终的结果还不错,遂记录一下.模型的效果不是最好的,但是胜在“端到端”, ...

  4. 《A Survey on Transfer Learning》迁移学习研究综述 翻译

    迁移学习研究综述 Sinno Jialin Pan and Qiang Yang,Fellow, IEEE 摘要:   在许多机器学习和数据挖掘算法中,一个重要的假设就是目前的训练数据和将来的训练数据 ...

  5. 迁移学习( Transfer Learning )

    在传统的机器学习的框架下,学习的任务就是在给定充分训练数据的基础上来学习一个分类模型:然后利用这个学习到的模型来对测试文档进行分类与预测.然而,我们看到机器学习算法在当前的Web挖掘研究中存在着一个关 ...

  6. 迁移学习(Transfer Learning)(转载)

    原文地址:http://blog.csdn.net/miscclp/article/details/6339456 在传统的机器学习的框架下,学习的任务就是在给定充分训练数据的基础上来学习一个分类模型 ...

  7. 迁移学习(Transfer Learning)

    原文地址:http://blog.csdn.net/miscclp/article/details/6339456 在传统的机器学习的框架下,学习的任务就是在给定充分训练数据的基础上来学习一个分类模型 ...

  8. 迁移学习(Transformer),面试看这些就够了!(附代码)

    1. 什么是迁移学习 迁移学习(Transformer Learning)是一种机器学习方法,就是把为任务 A 开发的模型作为初始点,重新使用在为任务 B 开发模型的过程中.迁移学习是通过从已学习的相 ...

  9. 【转载】 第四范式首席科学家杨强:AlphaGo的弱点及迁移学习的应对(附视频)

    原文地址: https://www.jiqizhixin.com/articles/2017-06-02-2 ============================================= ...

随机推荐

  1. 【JVM】类加载时机与过程

    虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制.下面来总结梳理类加载的五个阶段. 类加载发生在 ...

  2. APEX-数据导出/打印

    前言: 由于公司使用了Oracle APEX构建应用,且在APEX新版本v20.2版本中增强了相关报表导出数据相关功能:正好现在做的事情也需要类似的功能,就先来学习一下Oracle的APEX相关功能及 ...

  3. python常见的内置模块

    python的内置模块: os os.path sys time datetime hashlib base64 hmac random math 一.os模块: os模块是与操作系统交互的一个接口 ...

  4. Powerdesigner中表导出sql语句关于字段注释乱码的问题

    问题说明 注释中的汉字都变成了?,应该是编码的问题. declare @CurrentUser sysname select @CurrentUser = user_name() execute sp ...

  5. 【electron+vue3+ts实战便笺exe】一、搭建框架配置

    不要让自己的上限成为你的底线 前言 诈尸更新系列,为了跟上脚步,尝试了vue3,在学习vue3的时候顺便学习一手electron和ts,本教程将分别发布,源码会在最后的文章发布.因为还在开发中,目前也 ...

  6. 【超级经典】程序员装B指南(转)

    一.准备工作   "工欲善其事必先利其器." 1.电脑不一定要配置高,但是双屏是必须的,越大越好,能一个横屏一个竖屏更好.一个用来查资料,一个用来写代码.总之要显得信息量很大,效率 ...

  7. MongoDb学习三(spring-data-mongodb)

    本文采用2个种配置方式.xml配置 代码配置方式进行数据库的连接.实现简单的增删该查等一些操作.代码都有注释官方文档如下https://docs.spring.io/spring-data/mongo ...

  8. Phoneix(四)hbase导入数据同时与phoenix实现映射同步

    一.说明 先创建一个hbase表格,能够导入本地数据到hbase中,最后能够通过phoneix进行访问. 1.数据准备(10W条,样例如下),文件test.txt 0,20190520164020,1 ...

  9. 树莓派(4B)新手入门教程

    前期准备 必要物料 树莓派4B 主机 Type-C 电源 内存卡(8G+) 一般建议一步到位64G 系统镜像 镜像写入工具 下载地址 镜像下载 官方下载地址: https://www.raspberr ...

  10. JS 学习 一