2021-06-18初学pytorch建立cnn模型遇到的问题(自己建立数据集)

初次建立cnn模型

    • 问题一:图片维度与卷积维度不对应
      • 解决:
    • 问题2:pred= cnn_net(x)不正常工作
      • 解决
    • 问题3:预测值或标签是元组没有size属性
      • 解决
    • 成功
    • 训练
    • 如何使用dropout
      • 解决
    • 训练精度达不到95%
      • 小问题:drop的丢失率增加,测试集精度也会减少吗
    • 新问题:测试集可正常预测,单独拿出来就不能预测了
      • 解决(玄学?格式?)
    • 总结:大错没有,小错不断
    • 自我总结:敲代码千万别心急,越急越难受,越难搞完

数据集是根据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转换格式,记住,转换前将矩阵转换成浮点型数据。

自我总结:敲代码千万别心急,越急越难受,越难搞完

你可能感兴趣的:(应用,python,pytorch,人工智能)