首页 飞桨领航团 帖子详情
【AI达人养成营】-PP新手的两种基本CNN网络的实现方式(蝴蝶分类)
收藏
快速回复
飞桨领航团 文章AI达人创造营 441 0
【AI达人养成营】-PP新手的两种基本CNN网络的实现方式(蝴蝶分类)
收藏
快速回复
飞桨领航团 文章AI达人创造营 441 0

在作业四中我们尝试构造一个网络去实现 蝴蝶的分类:除了已经提供的数据读取处理部分。需要实现一些内容,下面是个人的一些体会:

  • 一.数据准备

1.在数据准备时,我们需要对数据做一些查看,因为在AIStudio上的交互,新手不是很熟练,建议可以下载数据集到本地。事实上我在做一些

训练时,都是先在本地把数据集处理好,挂到Aistuido上。

2.数据集分析,我试过 直接跳过这一步,直接丢到CNN(一个类Alex Net)中训练,基本上acc在50左右。所以建议还是要分析数据

(1).数据集中每个类别的样本数量级别,在各个类别的样本都很少的情况下,差距过大(少的几十张,对了接近200),很可能会在训练集acc很高,但是在验证集上很低(如下我写的一个类Alex,直接炼,训练集acc能到80,但是验证集基本在50左右)

(2).数据集的样本数量,这个就大力出奇迹,增广后再跑。但是在增广时候,涉及到一些颜色改变或者添加噪声的我没有尝试。可能数据即使增广后相对你选的模型样本数还是比较少,也会训练不好。所以我基本上只用了旋转加一些亮度/对比度

(3).数据集的操作部分应该也可以放在后面的构造数据集读取器部分,只是我习惯先处理出图像,在加载到读取器,方面查看一些增广效果

  • 二.自定义数据读取器

这部分就按照已经提供的方式做读取,图片类型转换跟归一化一下.

# 自定义数据读取器
class Reader(Dataset):
    def __init__(self, mode='train_set'):
        """
        初始化函数
        """
        self.data = []
        with open(f'{mode}_set.txt') as f:
            for line in f.readlines():
                info = line.strip().split('\t')
                if len(info) > 0:
                    self.data.append([info[0].strip(), info[1].strip()])  
        # self.transform = Compose([RandomRotation(25),RandomHorizontalFlip(0.5),ColorJitter()])

    def __getitem__(self, index):
        """
        读取图片,对图片进行归一化处理,返回图片和 标签
        """
        # data1 = paddle.uniform(IMAGE_SIZE, dtype='float32')
        image_file, label = self.data[index]  # 获取数据
        img = Image.open(image_file)  # 读取图片
        img = img.convert('RGB')
        img = img.resize((227, 227), Image.ANTIALIAS)  # 图片大小样式归一化

        # angle = np.random.randint(-14, 15)
        # img = img.rotate(angle)
        # v = random.random()
        # if v < 0.5:
        #     img = img.transpose(Image.FLIP_LEFT_RIGHT)
        # v = random.random()
        # if v < 0.5:
        #     brightness_delta = 0.225
        #     delta = np.random.uniform(-brightness_delta, brightness_delta) + 1
        # # delta值为0表示黑色图片,值为1表示原始图片
        #     img = ImageEnhance.Brightness(img).enhance(delta)
        # v = random.random()
        # if v < 0.5:
        #     contrast_delta = 0.5
        #     delta = np.random.uniform(-contrast_delta, contrast_delta) + 1
        # # delta值为0表示灰度图片,值为1表示原始图片
        #     img = ImageEnhance.Contrast(img).enhance(delta)


        img = np.array(img).astype('float32')  # 转换成数组类型浮点型32位
        img = img.transpose((2, 0, 1))     #读出来的图像是rgb,rgb,rbg..., 转置为 rrr...,ggg...,bbb...
        img = img/255.0  # 数据缩放到0-1的范围
        # img = img.shape()
        # print(img(0:3,:,:))
        return img, np.array(label, dtype='int64')

    def __len__(self):
  • 三.构造CNN网络并训练

(1)在这里我只构造了Alex-net,然后尝试修改卷积核跟步长大小或者添加一些层/BN/Dropout/修改FC层数去做尝试,因为时间原因没有尝试构造更复杂的网络,其实可以构造与下面使用的paddle自带的一些网络类型,以便更好的对比

class MyCNN(paddle.nn.Layer):
    def __init__(self):
        super(MyCNN,self).__init__()
        self.Conv_Pool_1 = paddle.nn.Sequential(#输入大小m*3*227*227
            paddle.nn.Conv2D(3,96,3,2,0),      #输出大小m*96*113*113
            paddle.nn.ReLU(),       #输出大小m*96*113*113
            paddle.nn.MaxPool2D(3, 2),  #输出大小m*96*56*56
            paddle.nn.BatchNorm2D(96))

        self.Conv_Pool_2 = paddle.nn.Sequential(
            paddle.nn.Conv2D(96,256,3,2,0),      #输出大小m*256*27*27
            paddle.nn.ReLU(),       #输出大小m*256*27*27
            paddle.nn.MaxPool2D(3, 2),  #输出大小m*256*13*13
            paddle.nn.BatchNorm2D(256))

        self.Conv_Pool_3 = paddle.nn.Sequential(
            paddle.nn.Conv2D(256,384,3,1,1),      #输出大小m*384*13*13
            paddle.nn.ReLU())       #输出大小m*384*13*13

        self.Conv_Pool_4 = paddle.nn.Sequential(
            paddle.nn.Conv2D(384,384,3,1,1),# 输出大小m*384*13*13
            paddle.nn.ReLU())       # 输出大小m*384*13*13

        self.Conv_Pool_5 = paddle.nn.Sequential(
            paddle.nn.Conv2D(384,256,3,1,1),#输出大小m*256*13*13
            paddle.nn.ReLU(),       #输出大小m*256*13*13
            paddle.nn.MaxPool2D(3, 2),  #输出大小m*256*6*6
            paddle.nn.BatchNorm2D(256))

        self.FullCon=paddle.nn.Sequential(
            paddle.nn.Linear(256*6*6, 4096),    # 输出大小m*4096
            paddle.nn.ReLU(),       #输出大小m*4096
            paddle.nn.Dropout(0.5),             # 输出大小m*4096
            # paddle.nn.Linear(4096, 4096),       #输出大小m*4096
            # paddle.nn.ReLU(),       # 输出大小m*4096
            # paddle.nn.Dropout(0.5),             # 输出大小m*4096
            paddle.nn.Linear(4096, 20))        #输出大小m*20
        self.flatten=paddle.nn.Flatten()

    def forward(self,x):
        x = self.Conv_Pool_1(x)
        x = self.Conv_Pool_2(x)
        x = self.Conv_Pool_3(x)
        x = self.Conv_Pool_4(x)
        x = self.Conv_Pool_5(x)
        x = self.flatten(x)
        x = self.FullCon(x)
        return x
paddle.summary(MyCNN(), (1, 3, 227, 227))
model = paddle.Model(MyCNN())

这边我记得在用的时如果paddle.summary放在实例化后面好像会报错

然后我们可以修改不同的learning_rate,epochs,batch_size做尝试,基本上跑完训练集acc在93,但是验证集只有73左右

 (2)使用官方的paddl高阶API中的model,我用了resnet 50(pretraned的)

class MyResCNN(paddle.nn.Layer):
    def __init__(self):
        super(MyResCNN,self).__init__()
        self.layer=paddle.vision.models.resnet50(pretrained=True)
        self.dropout=paddle.nn.Dropout(p=0.5)
        self.fc = paddle.nn.Linear(1000, 20)
    def forward(self,x):
        x=self.layer(x)
        x=self.dropout(x)
        x=self.fc(x)
        return x

 

这里加了一个dropout ,然后再接一个fc层数,将原来在imagenet的1000个类别的输出再连接到我们的20个输出?

后面还是同样的步骤:

实例化/构造optimizer/model.prepare/model.fit

在fit的时候可以通过参数去控制一些训练过程,比如保存模型的频率(很多时候没有必要每个epoch后都保存)等等

from paddle.static import InputSpec
input = InputSpec([None, 3, 227, 227], 'float32', 'image')
label = InputSpec([None, 1], 'int64', 'label')
paddle.summary(MyResCNN(), (1, 3, 227, 227))
model = MyResCNN()

model = paddle.Model(
    model,
    input, label)
optim = paddle.optimizer.Adam(
    learning_rate=0.0001, parameters=model.parameters())
model.prepare(
    optim,
    paddle.nn.CrossEntropyLoss(),
    paddle.metric.Accuracy())
model.fit(train_dataset,
          eval_dataset,
          epochs=20,
          batch_size=64,
          save_dir='resnet50_checkpoint',
          save_freq=5)
result = model.evaluate(eval_dataset, verbose=1)
print(result)

model.save('./Resbutterfly')  # 保存模型
  • 四.预测

这里我还是使用了dataset的方式,使用一个reader

class InferReader(Dataset):
    def __init__(self, imgpath='none'):
        super().__init__()
        self.img_paths = [imgpath]

    def __getitem__(self, index):
        # data1 = paddle.uniform(IMAGE_SIZE, dtype='float32')
        image_file = self.img_paths[index]  # 获取数据
        img = Image.open(image_file)  # 读取图片
        img = img.convert('RGB')
        img = img.resize((227, 227), Image.ANTIALIAS)  # 图片大小样式归一化
        img = np.array(img).astype('float32')  # 转换成数组类型浮点型32位
        img = img.transpose((2, 0, 1))     #读出来的图像是rgb,rgb,rbg..., 转置为 rrr...,ggg...,bbb...
        img = img/255.0  # 数据缩放到0-1的范围
        # img = img.shape()
        # print(img(0:3,:,:))
        return img
    def __len__(self):
        return len(self.img_paths)
from PIL import Image
inputd = InputSpec([None, 3, 227, 227], 'float32', 'image')
infer_path = 'data/Butterfly20_test/110.jpg'
infer_data = InferReader(infer_path)

model = paddle.Model(MyResCNN(),inputs=inputd)
model.load('./Resbutterfly.pdparams')
model.prepare()

result = model.predict(infer_data)[0] 
result = paddle.to_tensor(result)
result = np.argmax(result.numpy())
print('预测的结果为:', list(label_dic.keys())[result])  # 获取值
Image.open(infer_path)  # 显示图片
  • 五.很多都是自己的一些见解想象,有问题/有偏颇的话,希望看到的同学们能指正出来
0
收藏
回复
在@后输入用户全名并按空格结束,可艾特全站任一用户