一、神经网络基础
(1)块:在感知机模型里,神经网络由一层一层的神经元组成,块就是将这些层包装在一个函数里,然后调用的时候就可以直接调用该模块,不要用每次都写网络层。可以根据nn.Module父类自定义网络模块,也可以自定义前向传播,可以和nn.Sequential混合使用。
(2)参数管理:对卷积核初始化,也可以用shared参数绑定
(3)读写文件:可以用torch.save和torch.load存取,但是会重写文件数据,可以用csv包提供的函数一行一行的存入(f = open('data.csv','w') ;csv_writer = csv.writer(f);csv_writer.writerow(数据))。
(4)使用gpu时应注意参数和网络都应该转移到GPU上。
二、卷积神经网络
(1)全连接到卷积:全连接层是每一层的每一个神经元都是上层的所有神经元经过权重计算得到的,这样随着网络变深,数据量急剧增多,而且某些特征仅仅和周围神经元相关,所以根据平移不变性和局部性原则可以提取特征,进而提出卷积神经网络。
(2)卷积:全连接层的计算公式为,通过计算每一个块的表示形式,进而可以得出,这就保证某个元素是由上层对应点的周围经过权重得到的。过程如图所示:
(3)卷积层:对输入和卷积核权重进行互相关运算,并在添加标量偏置之后产生输出,注意的是每一个通道互相关运算后再加和才是新一层的神经元。特定的卷积核可以实现不同的功能。其中1*1卷积层计算发生在通道内,可以实现降维升维和跨通道信息交互。
(4)填充:因为卷积对图像矩阵运算时,会因为卷积核的大小让下一层图像矩阵少还会丢失边缘像素,通过对图像矩阵的外围填充部分0,可以保证矩阵大小不变和像素数据完整。如图所示:
(5)步幅:在计算互相关时,卷积窗口从输入张量的左上角开始,向下和向右滑动。如果元素数量过多,每次移动一步会导致网络中神经元过都,可以让窗口每次多滑动几步以减少数据量。如图所示:
(6)通道:多输入通道是一层神经网络时候上一层所有通道经过对应卷积权重计算相加得到的,多输出是因为网络要加深,需要多组卷积核计算增加特征提取。
(7)池化层:目的是为了降低卷积层对位置的敏感性,同时降低对空间降采样表示的敏感性。与卷积层类似,池化层运算符由一个固定形状的窗口组成,该窗口根据其步幅大小在输入的所有区域上滑动,为固定形状窗口遍历的每个位置计算一个输出。有最大值池化和平均值池化。注意的是池化层每个通道单独运算。
(8)经典神经网络:Lenet5--由两个卷积层和三个全连接层组成。
三、猫狗大战--经典图像分类题
1、框架搭建
(1)首先是对数据集处理并转化为像素矩阵,本次使用了torchvision.datasets.ImageFolder函数将训练集和验证集转化为像素矩阵,因为该函数对文件夹的子集为操作对象以子文件夹为标签,所以需要对训练集和验证集的照片分为cat和dog两个文件夹。图像处理时,因为图像不规则,长宽不一,所以需要对图像中心裁取。代码如下:
trans = transforms.Compose([transforms.CenterCrop(224),
transforms.ToTensor(), transforms.Normalize([0.5, 0.5, 0.5], [0.5, 0.5, 0.5])])
path = ".\\catdog"
# 读取路径下的子文件图像数据,以子文件夹为标签
train = {x: torchvision.datasets.ImageFolder(root=os.path.join(path, x), transform=trans) for x in ["train", "val"]}
test_last = {x: torchvision.datasets.ImageFolder(root=os.path.join(path, x), transform=trans) for x in ["test"]}
(2)对提取的数据分批次读取
batch = 128
data_loader_image = {x: torch.utils.data.DataLoader(dataset=train[x], batch_size=batch, shuffle=True) for x in
["train", "val"]}
data_loader_testimage = {x: torch.utils.data.DataLoader(dataset=test_last[x], batch_size=100, shuffle=False) for x in
["test"]}
(3)定义网络模型,本次使用了8层卷积层、5层池化层和3层全连接层
class net(nn.Module):
def __init__(self):
super().__init__()
self.net1 = nn.Sequential(
nn.Conv2d(3, 6, kernel_size=5, stride=1, padding=0),
nn.BatchNorm2d(6),
nn.ReLU(),
nn.MaxPool2d(2, 2),
) # (6,110,110)
self.net2 = nn.Sequential(
nn.Conv2d(6, 12, kernel_size=5, stride=1, padding=0),
nn.BatchNorm2d(12),
nn.ReLU(),
nn.MaxPool2d(2, 2), # 1
) # (12,53,53)
self.net3 = nn.Sequential(
nn.Conv2d(12, 12, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(12),
nn.ReLU()
) # (12,53,53)
self.net4 = nn.Sequential(
nn.Conv2d(12, 16, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(16),
nn.ReLU(),
nn.AvgPool2d(2, 2)
) # (16,26,26)
self.net5 = nn.Sequential(
nn.Conv2d(16, 32, kernel_size=3, stride=1, padding=0),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.AvgPool2d(2, 2)
) # (32,13,13)
self.net6 = nn.Sequential(
nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.AvgPool2d(2, 2)
) # (64,6,6)
self.net = nn.Sequential(
nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(64),
nn.ReLU()
)
self.Lin = nn.Linear(64 * 6 * 6, 120)
self.Lin1 = nn.Linear(120, 84)
self.Lin2 = nn.Linear(84, 2)
def forward(self, X):
X = X.view(-1, 3, 224, 224)
X = self.net1(X)
X = self.net2(X)
X = self.net3(X)
X = self.net4(X)
X = self.net5(X)
X = self.net6(X)
X = self.net(X)
X = X.view(-1, 64 * 6 * 6)
X = self.Lin(X)
X = F.relu(X)
X = self.Lin1(X)
X = F.relu(X)
X = self.Lin2(X)
return X
(4)将参数初始化并模型一起搬到GPU
def xavier(m):
if type(m) == nn.Linear:
nn.init.xavier_uniform_(m.weight)
epochs = 20
device = torch.device('cuda')
net_train = net()
net_train.apply(xavier)
net_train = net_train.to(device)
loss = nn.CrossEntropyLoss().to(device)
print(net_train)
optimizer = optim.Adam(net_train.parameters(), lr=1e-3)
train_loss = []#保存训练中的部分loss
train_acc = []#保存准确率
(5)对训练集训练模型
for epoch in range(epochs):
net_train.train()
itt = iter(data_loader_image["train"])
its = iter(data_loader_image["val"])
for i in range(157):
X_train, y_train = itt.next()
x = X_train
x = x.to(device)
label = y_train
label = label.to(device)
logits = net_train(x)
l = loss(logits, label)
optimizer.zero_grad()
l.backward()
optimizer.step()
if i % 10 == 0:
train_loss.append(l.item())
print(epoch, l.item())
net_train.eval()
with torch.no_grad():
correct = 0
total = 0
for j in range(16):
X_test, y_test = its.next()
x = X_test
x = x.to(device)
label = y_test
label = label.to(device)
logits = net_train(x)
pred = logits.argmax(dim=1)
correct += torch.eq(pred, label).float().sum().item()
total += x.size(0)
acc = correct / total
train_acc.append(acc)
print(acc)
(6)测试集预测,用csv库提供的函数将预测数据保存到文件
it = iter(data_loader_testimage["test"])
temp = 0
f = open('data.csv', 'w')
csv_writer = csv.writer(f)
with torch.no_grad():
for i in range(20):
T_data, T_y = it.next()
t = T_data.to(device)
logits = net_train(t)
pred = logits.argmax(dim=1)
for j in range(100):
csv_writer.writerow([temp, pred[j].item()])
temp += 1
2、结果
(1)训练集loss变化
(2)验证集准确率,最终能到85%左右
(3)测试集评分仅仅有51%
3、思考
网络模型在验证集表现很好,随着卷积层数的增加准确率从50%上升到85%,但是在测试集上总是在50%上下,说明网络繁华能力还是欠缺,具体问题在那还在思考测试。