初次建立cnn模型
数据集是根据Pytorch学习(三)定义自己的数据集及加载训练.来建立的数据集,其实官网有建立好的模板,但是介绍的太简单了,不太敢写(滑稽)
在自己建立cnn模型前,已根据pytorch官网学习了一遍,写了一遍cnn的代码,不过自己写一遍独有一番感受
以下是遇到的问题,建立数据集时也遇到了一些问题,有时间补充上:
问题一:图片维度与卷积维度不对应
RuntimeError: Given groups=1, weight of size [6, 3, 3, 3], expected input[64, 14, 22, 3] to have 3 channels, but got 14 channels instead
卷积权重为输入3通道, 输出6通道, 卷积核大小为3*3
但是认为输入数据为14通道,是不是应该把数据通道值放到shape[1]
现在输入张量维度为torch.Size([64, 3, 22, 14]),依旧报错
RuntimeError: expected scalar type Byte but found Float
RuntimeError:应为标量类型字节,但找到浮点
经检查,输入张量类型为torch.ByteTensor
链接: 参考网址.
根据参考网址,读取图片后数据应该改成float类型。
解决:
读取时
img = Image.open(path).convert('RGB')
img = np.array(img, dtype=np.float32)
此问题解决
问题2:pred= cnn_net(x)不正常工作
遇到下一个问题:
AttributeError: 'Linear' object has no attribute 'log_softmax'
发现以下错误:
x = self.fc2
解决
加个输入函数就好
问题3:预测值或标签是元组没有size属性
这个问题
loss = loss_fn(pred, y) # 这一步出现问题
AttributeError: 'tuple' object has no attribute 'size'
搜问题的时候很容易发现这篇文章pytorch 踩坑之’tuple’ object has no attribute ‘size’.
我还重安装了一下
pip innstall pytorch-summary
结果当然无效
我验证pred shape和type为
torch.Size([64, 36])
torch.FloatTensor
没有问题
接下来验证y
print(y.size())
print(y.type())
报错 AttributeError: ‘tuple’ object has no attribute ‘size’
所以是y的问题,y的类型为元组。
y = np.array(y, dtype=np.float32)
将y转换为np的数组
报错:
ValueError: could not convert string to float: 'U'
即字符U无法转换为float,我猜测是pytorch无法将u转换成独热编码搞得事,我搜的pytorch可以自动转换独热编码来着。
def one_hot(label):
"""
将字符标签转换为独热编码
:param label: str
:return: label_one_hot : tensor
"""
chr_y = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
y = np.arange(0, 36, 1)
y = np.array(y, dtype=np.int64)
y = torch.from_numpy(y)
one_hot_y = torch.nn.functional.one_hot(y)
side = chr_y.index(label)
label_one_hot = one_hot_y[side]
return label_one_hot
我将label转为one_hot编码输入后,出现如下错误
RuntimeError: 1D target tensor expected, multi-target not supported
即pytorch的交叉熵函数不支持输入独热编码,就跟之前查到的一样,她在程序中给你编好,你只需输入数字标签即可。也就是将所有输出可能排成一排[0,1,2 ··· , class_num]即可,不能输入字符型标签,也不能输入独热编码
解决
def one_hot(label):
将字符标签转换为独热编码
:param label: str
:return: label_one_hot : tensor
chr_y = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']
y = np.arange(0, 36, 1)
y = np.array(y, dtype=np.int64)
y = torch.from_numpy(y)
# one_hot_y = torch.nn.functional.one_hot(y)
side = chr_y.index(label)
label_one_hot = y[side]
print(label_one_hot)
print(label)
return label_one_hot
稍加修改即可
终于可以训练了,现在就是数据比较少,毕竟这是测试cnn程序,等我增加数据,训练完之后在补充。
另外,看来样本过少minibatch都不支持。
成功
epoch 1
-------------------------------
loss: 8.352506 [ 0/ 216]
Test Error:
Accuracy: 5.6%, Avg loss: 0.070053
训练
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), (2, 2))
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = self.fc2(x)
Epoch 19
-------------------------------
loss: 0.013064 [ 0/ 2368]
Test Error:
Accuracy: 86.5%, Avg loss: 0.019798
def forward(self, x):
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), (2, 2))
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
Epoch 19
-------------------------------
loss: 0.000194 [ 0/ 2368]
Test Error:
Accuracy: 88.5%, Avg loss: 0.024808
如何使用dropout
x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2))
x = F.max_pool2d(F.relu(self.conv2(x)), (2, 2))
x = x.view(-1, self.num_flat_features(x))
x = F.relu(self.fc1(x))
x = F.dropout(x, p=0.8)
x = F.relu(self.fc2(x))
x = F.dropout(x, p=0.8)
x = self.fc3(x)
这样使用drop后,不能正常训练了,
Epoch 5
-------------------------------
loss: 3.616909 [ 0/ 2368]
Test Error:
Accuracy: 3.9%, Avg loss: 0.117676
Epoch 6
-------------------------------
loss: 3.568411 [ 0/ 2368]
Test Error:
Accuracy: 3.0%, Avg loss: 0.117457
Epoch 7
-------------------------------
loss: 3.568021 [ 0/ 2368]
Test Error:
Accuracy: 4.3%, Avg loss: 0.117904
解决
p为丢失的概率,我搞的太大了,我以为是保留的可能性,太久忘记了
x = F.dropout(x, p=0.2) # p为丢失的概率,我搞的太大了,我以为是保留的可能性,太久忘记了
训练精度达不到95%
Epoch 15
-------------------------------
loss: 0.059839 [ 0/ 2368]
train Error:
train_Accuracy: 95.8%, Avg loss: 0.003490
Test Error:
Accuracy: 88.2%, Avg loss: 0.014631
通过查看训练集精度达到95.8,测试及精度才88.2%,准备提高drop的丢失率
-------------------------------
loss: 6.014652 [ 0/ 2368]
Test Error:
Accuracy: 8.2%, Avg loss: 0.110591
Epoch 2
-------------------------------
loss: 3.283289 [ 0/ 2368]
Test Error:
Accuracy: 13.5%, Avg loss: 0.101794
Epoch 3
-------------------------------
loss: 3.261986 [ 0/ 2368]
Test Error:
Accuracy: 20.1%, Avg loss: 0.097267
Epoch 4
-------------------------------
loss: 3.156312 [ 0/ 2368]
Test Error:
Accuracy: 20.7%, Avg loss: 0.092821
小问题:drop的丢失率增加,测试集精度也会减少吗
令p=1
Epoch 4
-------------------------------
loss: 3.577482 [ 0/ 2368]
train Error:
train_Accuracy: 1.4%, Avg loss: 0.112293
Test Error:
Accuracy: 0.0%, Avg loss: 0.118397
p=1时测试集为0,也就是说所有权重都丢失了,所以测试集时也用了drop???
我明明放cnn_net.eval()了
def train(dataloader, cnn_net, loss_fn, grads):
cnn_net.train()
size = len(dataloader.dataset)
for item, (X, y) in enumerate(dataloader):
pred = cnn_net(X)
loss = loss_fn(pred, y)
grads.zero_grad()
loss.backward()
grads.step()
if item % 100 == 0:
loss, current = loss.item(), item * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
def train_acc(dataloader, cnn_net, loss_fn):
cnn_net.eval()
size = len(dataloader.dataset)
test_loss, correct = 0, 0
with torch.no_grad():
for X, y in dataloader:
pred = cnn_net(X)
test_loss += loss_fn(pred, y).item()
correct += (pred.argmax(1) == y).type(torch.float).sum().item()
test_loss /= size
correct /= size
print(f"train Error: \n train_Accuracy: {(100 * correct):>0.1f}%, Avg loss: {test_loss:>8f} \n")
没找到为什么cnn_net.eval()不管用,只能用笨办法了
if drop:
x = F.dropout(x, p=0.2)
x = F.relu(self.fc2(x))
x = F.dropout(x, p=0.2)
x = self.fc3(x)
else:
x = F.relu(self.fc2(x))
x = self.fc3(x)
新问题:测试集可正常预测,单独拿出来就不能预测了
测试集
Predicted: "3", Actual: "3"
Predicted: "R", Actual: "R"
Predicted: "0", Actual: "0"
Predicted: "6", Actual: "6"
Predicted: "4", Actual: "4"
Predicted: "2", Actual: "2"
Predicted: "0", Actual: "0"
Predicted: "6", Actual: "6"
Predicted: "8", Actual: "8"
Predicted: "U", Actual: "U"
Predicted: "0", Actual: "0"
Predicted: "6", Actual: "6"
Predicted: "9", Actual: "9"
Predicted: "2", Actual: "2"
用训练好的模型测试单张图片
R
R
R
R
我将图片按照测试集的方式分割,保存文件目录和标签,导入cnn的数据集类,可以正常工作,但是直接将图片传给cnn就不行,我已经将图片按照数据集进行格式、维度的整理了。
图片处理:
transforms = ToTensor()
for img in photo_cut_list:
with torch.no_grad():
img = transforms(img)
img = torch.unsqueeze(img, 0)
print(img.size())
pred2 = forward(img, drop=False)
print(chr_y[pred2[0].argmax(0)])
output.append(chr_y[pred2[0].argmax(0)])
我的数据集:
def __getitem__(self, index):
path, label = self.img[index]
img = self.loader(path) # 读取出来维度为3, 22, 14 ?? 无法正常显示图片
img = np.array(img, dtype=np.float32)
label_one_hot = one_hot(label) # 不是独热编码,只是字符都变成了数字
if self.transform:
img = self.transform(img) # Totensor维度改变 torch.Size([3, 22, 14])
return img, label_one_hot, path
解决(玄学?格式?)
缺少一行代码,就这一行代码,直接无法识别图片内容,
真的玄学。我勒个去。我都想绕大圈解决这个问题了(将图片切割、保存,生成描述的txt文件,mydataset读取txt文件,输出x(这里是实际使用,标签y是固定的))
** img = np.array(img, dtype=np.float32)**
总结:大错没有,小错不断
已经建立过一次模型的情况下cnn结构框架很难出现问题,但是自己建立数据集,ε=(´ο`*)))唉,数据维度、形式、很容易出问题,还不太好找。
CNN数据集传递的数据维度为[通道,长或宽,长或宽],推荐使用ToTensor转换格式,记住,转换前将矩阵转换成浮点型数据。