Paddle实现迁移学习

92 阅读7分钟

Paddle实现迁移学习

项目说明,本项目是李宏毅老师在飞桨授权课程的作业解析
课程 传送门
该项目AiStudio项目 传送门
数据集 传送门

本项目仅用于参考,提供思路和想法并非标准答案!请谨慎抄袭!

迁移学习

三岁出品,必是精品!

迁移学习:先训练一个模型然后把该模型的参数给类似的项目直接进行训练,效果好极了!
基于这个原理我们开始思考流程


1、数据处理
2、网络定义
3、训练模型
4、固定参数
5、加载模型参数
6、进行迁移学习

本项目的最后结果差的离谱,原因原始模型就不好。
提高分数建议:
1、修改原始网络
2、提高原始模型质量
3、修改训练轮数
4、数据处理更加精准

作业5-迁移学习

项目描述

本作业的任务是迁移学习中的领域对抗性训练(Domain Adversarial Training)。

也就是左下角的那一块。

Domain Adaptation是让模型可以在训练时只需要 A dataset label,不需要 B dataset label 的情况下提高 B dataset 的准确率。 (A dataset & task 接近 B dataset & task)也就是给定真实图片 & 标签以及大量的手绘图片,请设计一种方法使得模型可以预测出手绘图片的标签是什么。

数据集介绍

这次的任务是源数据: 真实照片,目标数据: 手画涂鸦。

我们必须让model看过真实照片以及标签,尝试去预测手画涂鸦的标签为何。

资料位于’data/data58171/real_or_drawing.zip’

  • Training : 5000 张真实图片 + label, 32 x 32 RGB
  • Testing : 100000 张手绘图片,28 x 28 Gray Scale
  • Label: 总共需要预测 10 个 class。
  • 资料下载下来是以 0 ~ 9 作为label

特别注意一点: **这次的源数据和目标数据的图片都是平衡的,你们可以使用这个资料做其他事情。 **

项目要求

  • 禁止手动标记label或在网上寻找label
  • 禁止使用pre-trained model

数据准备

!unzip -oq /home/aistudio/data/data75815/real_or_drawing.zip
import os
import paddle
import paddle.vision.transforms as T
import numpy as np
from PIL import Image
import paddle.nn.functional as F
import random

数据处理

data_path = '/home/aistudio/real_or_drawing/train_data'  # 设置初始文件地址
character_folders = os.listdir(data_path)  # 查看地址下文件夹
character_folders
['2', '1', '5', '3', '8', '7', '6', '9', '4', '0']
# 新建标签列表
def img_list_text(train='train'):
    data_path = f'/home/aistudio/real_or_drawing/{train}_data'  # 设置初始文件地址
    character_folders = os.listdir(data_path)  # 查看地址下文件夹

    if(os.path.exists(f'./{train}_train_imglist.txt')):  # 判断文件是否存在
        os.remove(f'./{train}_train_imglist.txt')  # 删除文件
    if(os.path.exists(f'./{train}_test_imglist.txt')):  # 判断文件是否存在
        os.remove(f'./{train}_test_imglist.txt')  # 删除文件

    with open(f'./{train}_train_imglist.txt', 'w')as f_train:
        with open(f'./{train}_test_imglist.txt', 'w')as f_test:
            img_list = []
            for character_folder in character_folders:  #  循环文件夹列表  
                character_imgs = os.listdir(os.path.join(data_path,character_folder))  # 读取文件夹下面的内容
                count = 0
                for img in character_imgs:  # 循环图片列表
                    img_list.append(os.path.join(data_path,character_folder,img) + '\t' + character_folder + '\n')  # 写入地址及标签
                    count += 1
                print(character_folder,count)  # 查看各个目录的图片数量
            random.shuffle(img_list)  # 打乱列表
            count_1 = 0
            for img in img_list:  # 循环列表
                if count_1 < int(len(img_list)*0.8):  # 输出前80%为训练集
                    f_train.write(img)
                    count_1 += 1
                else:  # 剩下来的为验证集
                    f_test.write(img)
                    count_1 += 1
            print(len(img_list),int(len(img_list)*0.8))  # 查看总数量和训练集数量

img_list_text('train')  # 实例化数据
img_list_text('test')
2 500
1 500
5 500
3 500
8 500
7 500
6 500
9 500
4 500
0 500
5000 4000
0 100000
100000 80000
# 继承paddle.io.Dataset对数据集做处理
class FoodDataset(paddle.io.Dataset):
    """
    数据集类的定义
    """
    def __init__(self, mode='train'):
        """
        初始化函数
        """
        self.data = []
        with open(f'{mode}_imglist.txt') as f:  # 打开文件
            for line in f.readlines():  # 逐行读取
                info = line.strip().split('\t')  # 以\t进行分隔
                if len(info) > 0:
                    self.data.append([info[0].strip(), info[1].strip()])   # 写入文件
                      
    def __getitem__(self, index):
        """
        读取图片,对图片进行归一化处理,返回图片和 标签
        """
        image_file, label = self.data[index]  # 获取数据
        img = Image.open(image_file)  # 读取图片
        img = img.resize((28, 28), 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的范围
        return img, np.array(label, dtype='int64')

    def __len__(self):
        """
        获取样本总数
        """
        return len(self.data)

train_train_img = FoodDataset('train_train')
train_test_img = FoodDataset('train_test')
# 查看训练和测试数据的大小
print('train大小:', train_train_img.__len__())
print('eval大小:', train_test_img.__len__())

# 查看图片数据、大小及标签
for data, label in train_train_img:
    print(data[0][1])
    print(np.array(data).shape)
    print(label)
    break
train大小: 4000
eval大小: 1000
[1.         0.99215686 0.96862745 0.92156863 0.87058824 0.83137256 0.8039216  0.76862746 0.73333335 0.69803923 0.6784314  0.6784314 0.6784314  0.68235296 0.69803923 0.7176471  0.7372549  0.77254903 0.8        0.8156863  0.8156863  0.8039216  0.7921569  0.7647059 0.72156864 0.6745098  0.6117647  0.6313726 ]
(3, 28, 28)
6

定义网络

# 继承paddle.nn.Layer类,用于搭建模型
class MLPModel(paddle.nn.Layer):  # 继承paddle.nn.Layer类
    def __init__(self):
        super(MLPModel, self).__init__()
        self.flatten=paddle.nn.Flatten()  # 数据拉直
        self.hidden=paddle.nn.Linear(in_features=2352,out_features=128)  # 线性输入784输出128
        self.output=paddle.nn.Linear(in_features=128,out_features=10)  # 线性输入128输出10
        
    def forward(self, x):
        x=self.flatten(x)  #  拉直
        x=self.hidden(x)  # 经过隐藏层(线性层1)
        x=F.relu(x) # 经过激活层
        x=self.output(x)  # 经过输出层
        return x

model=paddle.Model(MLPModel())  # 实例化模型
model.summary((1,3,28,28))
---------------------------------------------------------------------------
 Layer (type)       Input Shape          Output Shape         Param #    
===========================================================================
   Flatten-6      [[1, 3, 28, 28]]        [1, 2352]              0       
   Linear-19        [[1, 2352]]            [1, 128]           301,184    
   Linear-20         [[1, 128]]            [1, 10]             1,290     
===========================================================================
Total params: 302,474
Trainable params: 302,474
Non-trainable params: 0
---------------------------------------------------------------------------
Input size (MB): 0.01
Forward/backward pass size (MB): 0.02
Params size (MB): 1.15
Estimated Total Size (MB): 1.18
---------------------------------------------------------------------------






{'total_params': 302474, 'trainable_params': 302474}

模型训练

# model = paddle.Model(network)  # 模型封装

# 配置优化器、损失函数、评估指标
model.prepare(paddle.optimizer.Adam(learning_rate=0.0001, parameters=model.parameters()), 
              paddle.nn.CrossEntropyLoss(), 
              paddle.metric.Accuracy())

# 训练可视化VisualDL工具的回调函数
visualdl = paddle.callbacks.VisualDL(log_dir='visualdl_log')   

# 启动模型全流程训练
model.fit(train_train_img,  # 训练数据集
        train_test_img,   # 评估数据集
        epochs=5,       # 训练的总轮次
        batch_size=64,  # 训练使用的批大小
        verbose=1,      # 日志展示形式
        callbacks=[visualdl])  # 设置可视化
The loss value printed in the log is the current step, and the metric is the average value of previous step.
Epoch 1/5
step 63/63 [==============================] - loss: 1.4922 - acc: 0.4325 - 13ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 16/16 [==============================] - loss: 2.1877 - acc: 0.4000 - 12ms/step        
Eval samples: 1000
Epoch 2/5
step 63/63 [==============================] - loss: 1.6968 - acc: 0.4325 - 13ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 16/16 [==============================] - loss: 2.1877 - acc: 0.4000 - 12ms/step        
Eval samples: 1000
Epoch 3/5
step 63/63 [==============================] - loss: 1.6774 - acc: 0.4325 - 13ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 16/16 [==============================] - loss: 2.1877 - acc: 0.4000 - 12ms/step        
Eval samples: 1000
Epoch 4/5
step 63/63 [==============================] - loss: 1.6447 - acc: 0.4325 - 14ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 16/16 [==============================] - loss: 2.1877 - acc: 0.4000 - 12ms/step        
Eval samples: 1000
Epoch 5/5
step 63/63 [==============================] - loss: 1.7489 - acc: 0.4325 - 13ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 16/16 [==============================] - loss: 2.1877 - acc: 0.4000 - 13ms/step        
Eval samples: 1000
model.save('finetuning/mnist')  # 保存模型

迁移学习

# 数据处理
test_train_img = FoodDataset('test_train')
test_test_img = FoodDataset('test_test')
# 查看训练和测试数据的大小
print('train大小:', train_train_img.__len__())
print('eval大小:', train_test_img.__len__())

# 查看图片数据、大小及标签
for data, label in train_train_img:
    print(data[0][1])
    print(np.array(data).shape)
    print(label)
    break
train大小: 80000
eval大小: 20000
[0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.]
(3, 28, 28)
0
# 模型封装,为了后面保存预测模型,这里传入了inputs参数
model_2 = paddle.Model(MLPModel())  # 实例化模型

# 加载之前保存的阶段训练模型
model_2.load('finetuning/mnist')

# 模型配置
model_2.prepare(paddle.optimizer.Adam(learning_rate=0.001, parameters=network.parameters()),
                paddle.nn.CrossEntropyLoss(),
                paddle.metric.Accuracy())

# 模型全流程训练
model_2.fit(test_train_img, 
            test_test_img,
            epochs=2,
            batch_size=64,
            verbose=1)
The loss value printed in the log is the current step, and the metric is the average value of previous step.
Epoch 1/2
step 1250/1250 [==============================] - loss: 2.6177 - acc: 0.0119 - 11ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 313/313 [==============================] - loss: 2.6651 - acc: 0.0119 - 12ms/step        
Eval samples: 20000
Epoch 2/2
step 1250/1250 [==============================] - loss: 2.8404 - acc: 0.0119 - 9ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 313/313 [==============================] - loss: 2.6651 - acc: 0.0119 - 8ms/step         
Eval samples: 20000

验证结果

result = model.evaluate(test_test_img, verbose=1)  # 验证

print(result)
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 20000/20000 [==============================] - loss: 2.4581 - acc: 0.0119 - 2ms/step         
Eval samples: 20000
{'loss': [2.4580889], 'acc': 0.0119}

作者简介

作者:三岁
经历:自学python,现在混迹于paddle社区,希望和大家一起从基础走起,一起学习Paddle
csdn地址:blog.csdn.net/weixin_4562…
我在AI Studio上获得至尊等级,点亮9个徽章,来互关呀~ aistudio.baidu.com/aistudio/pe…

传说中的飞桨社区最菜代码人,让我们一起努力!
记住:三岁出品必是精品 (不要脸系列