在作业四中我们尝试构造一个网络去实现 蝴蝶的分类:除了已经提供的数据读取处理部分。需要实现一些内容,下面是个人的一些体会:
- 一.数据准备
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) # 显示图片
- 五.很多都是自己的一些见解想象,有问题/有偏颇的话,希望看到的同学们能指正出来