本节介绍代码结构
库的安装跳过不介绍:)
from torchvision import datasets, transforms
from torch.utils.data import DataLoader, Dataset
import torch
import torch.nn as nn
import os
from PIL import Image
这是个好习惯,比赛用不到,比赛设备只给得起CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
这里是各种准备工作,创建对象声明变量,有必要介绍一下transforms
你可以在这里准备好你要对你的数据集所做的变换,比赛只要求大小变换,实际上Normalize
归一化的使用我认为才是最重要的。
label_train_file = open("./数据与代码/第四部分/train.txt")
labe_test_file = open("./数据与代码/第四部分/test.txt")
pic_path = os.path.join(os.getcwd(), './数据与代码/第四部分/imgdata')
data_transform = transforms.Compose([transforms.ToTensor(),
transforms.Resize([64, 64])])
这里都很基础啊,一行一行读txt,依据空格拆分。
数据集标签文件结构:
读图片然后使用到上一步设定好的data_transform
就行图像转换。
可以看到我的代码里面的train_data
和test_data
,这一步骤里面的需要得到东西,这个列表里面元素你可以用键值对理解,“键”指的是图像的tensor
,“值”指的是标签的tensor
,这里是多标签,mask_len记录标签数。
train_data = []
train_data_len = 0
for i, v in enumerate(label_traian_file):
mid = v.strip().split()
img_file = mid[0]
labels = mid[1:len(mid)]
label = [int(i) for i in labels]
img = Image.open(os.path.join(pic_path, img_file))
img = data_transform(img)
label = torch.tensor(label)
train_data.append(tuple([img, label]))
train_data_len += 1
test_data = []
test_data_len = 0
for i, v in enumerate(labe_test_file):
mid = v.strip().split()
img_file = mid[0]
labels = mid[1:len(mid)]
label = [int(i) for i in labels]
img = Image.open(os.path.join(pic_path, img_file))
img = data_transform(img)
label = torch.tensor(label)
test_data.append(tuple([img, label]))
# print(test_data[0])
test_data_len += 1
mask_len = len(v.strip().split()[1:len(v.strip().split())])
print(mask_len, train_data_len, test_data_len)
***这里输出结果是10 100 50***
将train_data
和test_data
输入到DataLoader中做准备,它会将图片分成train_data
/batch_size
块,每块batch_size
张图片,这里多少取决于你的内存或者显存。
train_loader = DataLoader(dataset=train_data, batch_size=16, shuffle=True, drop_last=True)
test_loader = DataLoader(dataset=test_data, batch_size=2, shuffle=True, drop_last=True)
接下来是深度学习网络结构(其实这个我感觉一点也不深,太深了比赛数据又会过拟合,就我这个几层都过拟合了,比赛很正规),这里你可以不会,在准备网络赛的时候写好,带上纸质资料就行,我会在后面介绍原理。
class NET(nn.Module):
def __init__(self, len):
super(NET, self).__init__()
self.conv1 = nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=8, kernel_size=5, stride=2, padding=0),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2)
)
self.conv2 = nn.Sequential(
nn.Conv2d(in_channels=8, out_channels=16, kernel_size=3, stride=1, padding=0),
nn.ReLU(),
)
self.conv3 = nn.Sequential(
nn.Conv2d(in_channels=16, out_channels=16, kernel_size=3, stride=1, padding=0),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2)
)
self.conv4 = nn.Sequential(
nn.Conv2d(in_channels=16, out_channels=24, kernel_size=3, stride=1, padding=0),
nn.ReLU(),
)
self.fc = nn.Linear(24 * 3 * 3, len)
self.sigmoid = nn.Sigmoid()
def forward(self, x):
x = self.conv1(x)
x = self.conv2(x)
x = self.conv3(x)
x = self.conv4(x)
x = x.view(x.size(0), -1)
output = self.fc(x)
# output = self.sigmoid(x)
return output
严谨一点这叫声明一个NET对象net。
看到device我想提一嘴,在你的学习过程中device不注意好的话,可能会出现两个张量一个在CPU一个在GPU的问题,当然比赛电脑是不会出现的:)
net = NET(mask_len)
net.to(device)
这两个分别是优化器和损失函数,方法有很多种但数学原理不介绍,就是一堆公式输入x得到y的含义,会在后面介绍用法。
optimizer = torch.optim.Adam(net.parameters(), lr=0.01)
loss_func = torch.nn.L1Loss()
你可以理解成固定代码格式,但是计算预测值和正确值之间的差值,这里我用的是cosine_similarity
计算余弦相似度来代表准确率,根据需要灵活变通,不需要多合理评委不一定懂:),假设是单标签多分类,torch.eq
比较常用。epoch是迭代次数,loss和优化器在这里起到促进作用,可以理解成他们不断告诉上一次epoch,这次学习出现这样那样的错误,要这样那样改进!他们应该在训练阶段的每一次epoch
的时候都存在。
loss就是可以代表错误程度,假设它学习能力不错,它的错误程度就会越来越小,直到平缓,这个时候称为模型达到收敛,训练结束,一般这段平缓时区的准确率,可以代表这个模型的准确率。
通常会画图(说不定还会出个解读教程嗯)表示每一次的loss和acc,当然该比赛不需要,下面这个代码还不算常规(针对比赛我不写不需要我们输出的东西,避免不必要的麻烦),因为我们会在训练时每一次epoch计算acc和loss,通常这里还会涉及到验证集,但是去年的比赛只存在训练集和测试集,后面我会详细介绍。
for epoch in range(10):
train_loss = 0.0
for index, (batch_x, batch_y) in enumerate(train_loader):
out = net(batch_x.to(device))
loss = loss_func(out, batch_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_loss += loss.item()
print('Epoch:{} Train Loss:{}'.format(epoch + 1, train_loss / len(train_loader)))
net.eval()
with torch.no_grad():
acc = 0.0
for index, (batch_x, batch_y) in enumerate(test_loader):
pre = net(batch_x.to(device))
similar = torch.cosine_similarity(batch_y.to(device), pre)
acc += similar.sum().item()
print("acc", acc / test_data_len)
看到这里如果你感觉你好像懂了,但是没有完全懂,这个好像懂了,就差不多可以跑通代码了,我在22年初刚刚接触它的时候,跑通的第一个代码差不多了解的这么多,当然比这个比赛的这个代码复杂很多:)
如果感兴趣就往后面看吧!
我修炼阶段学习的是李沐老师的视频https://space.bilibili.com/1567748478?spm_id_from=333.337.0.0
再看完我之后的介绍后还想学习数学原理的话强烈推荐!!