##数据准备 解压缩后首先查看数据img和label,学的时候是通过手写数据集的例子学习的(b站小土堆的视频里有类似的数据处理操作),所以没有什么头绪,选择求助csdn,发现可以把所有的放在一个文档里,用python的文件操作来完成,但是后来又发现label里有没用的复制件qwq,但是处理并不难,直接全跑一遍把带括号的全部删除即可。 代码如下

import os
root = "D:\RM数据\RM_train_data"
train_r = os.path.join(root, "train")
test_r = os.path.join(root, "valid")
test_lab = os.path.join(train_r, "labels")# train
test_lab1 = os.path.join(test_r, "labels")
lab1 = os.listdir(test_lab1)
labr = []
for i in lab1:
    if('(' not in i):
        labr.append(i)




lab = os.listdir(test_lab)
print(lab)
labt = []

for i in lab:
    if ('('not in i):
        labt.append(i)

print(labt)#把所有的无用的文件删除,测试

def solve(r,l):
    file = open(os.path.join(r+"lab.txt"),'a')
    for i in l:
        file2 = open(os.path.join(r,"labels",i),'r')
        for line in file2:
            thing = line.split()
            name  = i.replace('.txt','.jpg')
            file.write(name+" "+thing[0]+" "+thing[1]+" "+thing[2]+" "+thing[3]+" "+thing[4]+"\n")
    file.close()

solve(train_r,labt)
solve(test_r,labr)

##dataset和dataloader的使用 二者其实就是pytorch提供的一个录入数据的方法,csdn搜索一下使用方法即可。(虽然但是还是挺容易搞错的) ###dataset init的意义感觉就是把所有的数据的参数搞在一起 getitem的作用差不多就是把数据集中的东西单独搞出来,用来提供给后面的操作 ###dataloader 这个的用法就很简单,就是一个数据的上传器,里面的参数要注意,训练集和测试集的参数不是一样的。

class ds(Dataset):
    def __init__(self, path, train=True, transform=None):
        self.path = path
        file = open(os.path.join(path, "lab.txt"), 'r')
        dt = file.readlines()
        ip = []  # image path
        label = []
        x = []
        y = []
        l = []
        w = []
        n = []

        for i in dt:
            i = i.strip()
            ins = i.split(" ")
            x.append(float(ins[2]))
            y.append(float(ins[3]))
            ip.append(os.path.join(path, 'images', ins[0]))
            label.append(ins[1])
            n.append(ins[0])
            l.append(float(ins[4]))
            w.append(float(ins[5]))
        self.x = x
        self.y = y
        self.l = l
        self.w = w
        self.ip = ip
        self.n = n
        self.label = label
        self.transform = transform

    def __getitem__(self, item):
        img = self.ip[item]
        label = self.label[item]
        img = Image.open(img)
        img = img.convert("RGB")  # PNG 为四通道 rgb和透明
        x = self.x[item]
        y = self.y[item]
        l = self.l[item] / 2
        w = self.w[item] / 2
        img = img.crop((int((x - l) * img.size[0]), int((y - w) * img.size[1]), int((x + l) * img.size[0]),int((y + w) * img.size[1])))#图像的剪切

        img = self.transform(img)
        label = np.array(label)
        label = label.astype(np.int64)
        label = torch.from_numpy(label)  # 转tensor
        # 张量转换参考以下博客
        # https://blog.csdn.net/qq_24193303/article/details/121829719#:~:text=%E6%88%91%E4%BB%AC%E9%80%9A%E5%B8%B8%E4%BC%9A%E5%B0%86one-hot%E7%B1%BB%E5%9E%8B%E7%9A%84%E6%A0%87%E7%AD%BE%E4%BB%A5list%E7%9A%84%E5%BD%A2%E5%BC%8F%E8%AF%BB%E5%8F%96%EF%BC%8C%E7%84%B6%E5%90%8E%E7%9B%B4%E6%8E%A5%E5%B0%B1%E5%8F%AF%E4%BB%A5%E4%BD%BF%E7%94%A8torch.LongTensor%20%28label%29%E8%BF%9B%E8%A1%8C%E8%BD%AC%E6%8D%A2%EF%BC%8C%E7%B1%BB%E5%9E%8B%E4%B8%BAtorch.int64%20label%3D,%5B%201%2C%200%2C%200%5D
        return img, label

    def __len__(self):
        return len(self.label)


trp = r"D:\RM数据\RM_train_data\train"
tep = r"D:\RM数据\RM_train_data\valid"
trd = ds(trp, train=True, transform=transforms)
trl = DataLoader(trd, shuffle=True, batch_size=64, drop_last=True)
ted = ds(tep, train=False, transform=transforms)
tel = DataLoader(ted, shuffle=False, batch_size=64, drop_last=True)
#droplast是发现咱们的数据的数量不能整除,在后面神经网络的处理中就会有一些参数不匹配的问题,所以我选择直接抛弃它

##神经网络的搭建 首先先认识一下神经网络中各个层的作用 ###卷积层 卷积层的作用是提取输入图片中的信息,这些信息被称为图像特征,这些特征是由图像中的每个像素通过组合或者独立的方式所体现,比如图片的纹理特征,颜色特征。 ###池化层 池化层的作用是对卷积层中提取的特征进行挑选 常见的池化操作有最大池化和平均池化,池化层是由n×n大小的矩阵窗口滑动来进行计算的,类似于卷积层,只不过不是做互相关运算,而是求n×n大小的矩阵中的最大值、平均值等 ###全连接层 池化层的后面一般接着全连接层,全连接层将池化层的所有特征矩阵转化成一维的特征大向量,全连接层一般放在卷积神经网络结构中的最后,用于对图片进行分类,到了全连接层,我们的神经网络就要准备输出结果了

个人理解:卷积层的作用是提取特征,池化层的作用大体上是用来深化特征,而全链接层的作用基本上是用来降低维度开始准备最后的输出过程。

###本次我所用的神经网络 总体上是三层卷积、一层最大池化层、一层线性层

class md(torch.nn.Module):
    def __init__(self):
        super(md, self).__init__()
        self.c1 = torch.nn.Conv2d(3, 10, kernel_size=5)
        self.c2 = torch.nn.Conv2d(10, 20, kernel_size=5)
        self.c3 = torch.nn.Conv2d(20,50,kernel_size=5)


        self.p = torch.nn.MaxPool2d(2)

        self.l = torch.nn.Linear(14450, 12)

    def forward(self, a):
        
        a = F.relu(self.p(self.c1(a)))
        a = F.relu(self.p(self.c2(a)))
        a = F.relu(self.p(self.c3(a)))
        a = F.view(64,-1)
        a = self.l(a)
        return a

##在模型中的几个函数 ###1.relu 是一种激活的函数,激活函数在激发隐藏节点以产生更理想的输出方面起着重要作用。 激活函数的主要目的是将非线性特性引入模型。个人理解:激活函数就是在样本中寻找更高的非线性的特征,进而提取,提高判断的能力和准确性。 ###2.view 相当于reshape,在我的模型中就是一个把batchsize统一的作用。

##神经网络搭建的基本架构 首先是写下一个神经网络的类,然后就是写自己模型中的不同层,基本上是卷积层到池化层中间加入激活函数来加入非线性维度进行升高维度。(当然我现在基本上在停留在最基础的层面,所以我的理解应该会和真理有一定的偏差)

##神经网络搭建的注意事项 ###1.注意数据的正确加载(训练过程中也是 最开始数据没有处理好就一直出bug(甚至后来在训练的过程里我还在一个小错误上耗费了快2小时:我在传入训练集的数据的时候把数据集的dataset传进去,一直在我的神经网络那报错,痛苦!

###2.注意层的选取 我最开始的卷积层好像是只有一层,加了个池化层一个激活函数,最后训练的正确率好像才有20%不到,后来就是一直在加层数,特别注意,在卷积层和池化层的kernel的选择,我在尝试了好多次才得到现在这个参数

###3.线性层的参数选取 在线性层那的参数最开始根本不会算,我还去学了一下(虽然忘记的贼快),但是在搜索过程中突然发现了一个贼好的技巧,就是编译器会自己报错,所以根本不用手算。就是这一步:

self.l = torch.nn.Linear(14450, 12)

###4.神经网络有时候也不是越复杂越好 我加了一层卷积层后正确率不增反而降低,在网上搜索后可能是以下几个问题: ####梯度消失和梯度爆炸 但是据说用relu激活的话还好 ####过拟合 随着网络层数的加深,参数变多,神经网络的拟合能力变得很强, 这也就意味着其表达出来的函数会更复杂, 而如果对于简单问题如二分类问题, 采用过于复杂的函数, 是极容易导致过拟合的。而对于复杂问题如图像问题以及语言问题, 由于其本身的复杂度, 网络的深度一般是越深, 效果越好。(以上来自知乎)图像处理过程应该有可能不是这个原因。 ####退化 从卷积和池化啥的原理我们肯定能发现,每次操作的过程中都会导致图像的内容丢失。我觉得加层卷积正确率降低有可能是这个原因。

##神经网络的改进方法 ###1.增加网络的深度 最开始的一层网络都是很低的正确率,两层网络的正确率虽然在五轮训练下能达到88左右,但是三层卷积就能达到91左右。神经网络太浅会导致欠拟合,也就是特征提取不完全(个人理解 ###2.增加训练的次数 在50轮的训练后正确率能达到96左右,应该算是一个还不错的结果了

###3.注意参数的选择 在各层次的参数选择上要下点功夫,但我暂时还不太清楚该怎么搞,我是一次次尝试出来的。(qwq)