机器学习数据科学基础——神经网络基础实验

Posted 云曦智划

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了机器学习数据科学基础——神经网络基础实验相关的知识,希望对你有一定的参考价值。

【机器学习】数据科学基础——神经网络基础实验


活动地址:[CSDN21天学习挑战赛](https://marketing.csdn.net/p/bdabfb52c5d56532133df2adc1a728fd)


作者简介:在校大学生一枚,华为云享专家,阿里云星级博主,腾云先锋(TDP)成员,云曦智划项目总负责人,全国高等学校计算机教学与产业实践资源建设专家委员会(TIPCC)志愿者,以及编程爱好者,期待和大家一起学习,一起进步~
.
博客主页ぃ灵彧が的学习日志
.
本文专栏机器学习
.
专栏寄语:若你决定灿烂,山无遮,海无拦
.

文章目录


前言

什么是神经网络?

神经网络是一门重要的机器学习技术,它是目前人工智能领域内最为火热的研究方向——深度学习技术的基础。神经网络是一种模仿动物神经网络行为特征,进行分布式并行信息处理的算法数学模型,也是我们后续学习自然语言处理和视觉图像处理的基础


一、基于全连接神经网络实型房价预测

(一)、数据加载及预处理

  1. 导入相关包:
# import paddle.fluid as fluid
import paddle
import numpy as np
import os
import matplotlib.pyplot as plt
  1. 设置paddle默认的全局数据类型为float64
#设置默认的全局dtype为float64
paddle.set_default_dtype("float64")
#下载数据
print('下载并加载训练数据')
train_dataset = paddle.text.datasets.UCIHousing(mode='train')
eval_dataset = paddle.text.datasets.UCIHousing(mode='test')
train_loader = paddle.io.DataLoader(train_dataset, batch_size=32, shuffle=True)
eval_loader = paddle.io.DataLoader(eval_dataset, batch_size = 8, shuffle=False)

(二)、模型配置

线性回归本质上是一层不带激活函数的全连接层,因此本实验使用

paddle.nn.Linear(in_features,out_features,weight_attr=None,nias_attr=None,name=None)

来实现线性变换,其中,in_features为输入特征的维度,out_features为输出特征的维度,weight_attr指定权重参数的属性,表示使用默认的权值参数属性,将权重参数初始化为0,bias_attr指定偏置参数的属性,设置为False时,表示不会为该层添加偏置,name用于网络层输出的前缀标识,在自定义网络模型时,应当继承paddle.nn.Layer类,该类属于基于OOD实现的动态图,实现了训练模式与验证模式,训练模型会执行反向传播,而验证模式不包含反向传播,同时也为dropout等训练,验证时不同的操作提供了支持。在神经网络中,从输入到输出的过程称为网络的前向计算,在飞浆中,可用forward关键字标识,forward()函数定义函数从前到后的完整计算过程,是实现网络框架最重要的环节。


定义全连接网络:

# 定义全连接网络
class Regressor(paddle.nn.Layer):
    def __init__(self):
        super(Regressor, self).__init__()
        # 定义一层全连接层,输出维度是1,激活函数为None,即不使用激活函数
        self.linear = paddle.nn.Linear(13, 1, None)
    
    # 网络的前向计算函数
    def forward(self, inputs):
        x = self.linear(inputs)
        return x

(三)、模型训练

本实验中使用

paddle.optimizer.SGD(learning_rate=0.001,parameters=None,weight_decay=None,grad_clip=None,name=None)

进行优化,其中learning_rate为学习率,也就是参数梯度的更新步长,parameters指定优化器需要优化的参数,weight_decay为权重衰减系数,grad_clip为梯度裁减的策略,支持三种裁剪策略:paddle.nn.ClipGradByGlobalNorm、paddle.nn.ClipGradByNorm、paddle.nn.ClipGradByValue,梯度裁剪将梯度值阶段约束在一个范围内,防止使用深度网络时出现梯度爆炸的情况,默认值为None,此时将不进行梯度裁剪。定义好模型、损失函数和优化器之后,将数据分批送入模型中,并执行梯度反向传播更新参数(loss.backward()),达到训练目的,模型训练结束后,调用paddle.save()保存模型,后续进行预测时,只需要将训练好的模型参数加载到模型中,便可利用训练数据提取到的规律对测试数据进行预测。

代码如下:

Batch=0
Batchs=[]
all_train_accs=[]
def draw_train_acc(Batchs, train_accs):
    title="training accs"
    plt.title(title, fontsize=24)
    plt.xlabel("batch", fontsize=14)
    plt.ylabel("acc", fontsize=14)
    plt.plot(Batchs, train_accs, color='green', label='training accs')
    plt.legend()
    plt.grid()
    plt.show()

all_train_loss=[]
def draw_train_loss(Batchs, train_loss):
    title="training loss"
    plt.title(title, fontsize=24)
    plt.xlabel("batch", fontsize=14)
    plt.ylabel("loss", fontsize=14)
    plt.plot(Batchs, train_loss, color='red', label='training loss')
    plt.legend()
    plt.grid()
    plt.show()

model=Regressor() # 模型实例化
model.train() # 训练模式
mse_loss = paddle.nn.MSELoss()
opt=paddle.optimizer.SGD(learning_rate=0.0005, parameters=model.parameters())

epochs_num=200 #迭代次数
for pass_num in range(epochs_num):
    for batch_id,data in enumerate(train_loader()):
        image = data[0]
        label = data[1]
        predict=model(image) #数据传入model
        # print(predict)
        # print(np.argmax(predict,axis=1))
        loss=mse_loss(predict,label)
        # acc=paddle.metric.accuracy(predict,label.reshape([-1,1]))#计算精度
        # acc = np.mean(label==np.argmax(predict,axis=1))
        
        if batch_id!=0 and batch_id%10==0:
            Batch = Batch+10
            Batchs.append(Batch)
            all_train_loss.append(loss.numpy()[0])
            # all_train_accs.append(acc.numpy()[0]) 
            print("epoch:,step:,train_loss:".format(pass_num,batch_id,loss.numpy()[0])  )      
        loss.backward()       
        opt.step()
        opt.clear_grad()   #opt.clear_grad()来重置梯度
paddle.save(model.state_dict(),'Regressor')#保存模型
draw_train_loss(Batchs,all_train_loss)

模型训练过程中部分输出如下图1-1所示:


(四)、模型评估

  1. 模型训练结束后,根据保存的损失值的中间结果,绘制损失值随模型迭代次数的变化过程:
def draw_train_acc(Batchs,train_accs):
	title="training accs"
	plt.title(title,fontsize=24)
	plt.xlabel("batch",fontsize=14)
	plt.ylabel("acc",fontsize=14)
	plt.plot(Batchs,train_accs,color='green',label='training accs')
	plt.legend()
	plt.grid()
	plt.show()
draw_train_loss(Batchs,all_train_loss)

模型损失值随迭代次数变化趋势如下图1-2所示:


  1. 为了判断上述模型的性能,可在验证集上进行验证。首先将前面步骤保存的训练好的参数加载到新实例化的模型中,然后启动验证模型,将验证数据批量输入到网络中进行损失值计算,并输出模型在验证集上的损失值:
#模型评估
para_state_dict = paddle.load("Regressor") 
model = Regressor()
model.set_state_dict(para_state_dict) #加载模型参数
model.eval() #验证模式

losses = []
infer_results=[]
groud_truths=[]
for batch_id,data in enumerate(eval_loader()):#测试集
    image=data[0]
    label=data[1] 
    groud_truths.extend(label.numpy())    
    predict=model(image) 
    infer_results.extend(predict.numpy())      
    loss=mse_loss(predict,label)
    losses.append(loss.numpy()[0])
    avg_loss = np.mean(losses)
print("当前模型在验证集上的损失值为:",avg_loss)

输出结果如下图1-3所示:


  1. 绘制模型预测结果与真实值之间的差异,当模型的预测值等于真实值时,模型的预测效果是最优的,但是这种情况几乎不可能出现,因此作为对照,可以观察预测值与真实值构成的坐标点位于y=x直线的位置,判断模型性能的好坏,代码实现及图示如下:
#绘制真实值和预测值对比图
def draw_infer_result(groud_truths,infer_results):
    title='Boston'
    plt.title(title, fontsize=24)
    x = np.arange(1,20) 
    y = x
    plt.plot(x, y)
    plt.xlabel('ground truth', fontsize=14)
    plt.ylabel('infer result', fontsize=14)
    plt.scatter(groud_truths, infer_results,color='green',label='training cost') 
    plt.grid()
    plt.show()

draw_infer_result(groud_truths,infer_results)

真实值和预测值对比图如下图1-4所示:


小结

上述方法获得模型的拟合能力并没有达到最优,仍然具有很大的优化空间。线性回归算法只能处理线性可分的数据,对于线性不可分数据,在传统机器学习算法中,需要使用对数线性回归、广义线性回归或者其它回归算法,但是在神经网络中,可以通过添加激活函数、加深网络深度,实现任意函数的拟合。


二、基于全连接神经网络实现宝石分类

(一)、数据加载及预处理

  1. 加载必要的包:
import os
import zipfile
import random
import json
import cv2
import numpy as np
from PIL import Image
import paddle
import matplotlib.pyplot as plt
from paddle.io import Dataset
  1. 参数配置:
'''
参数配置
'''
train_parameters = 
    "input_size": [3, 224, 224],                           #输入图片的shape
    "class_dim": 25,                                     #分类数
    "src_path":"data/data55032/archive_train.zip",       #原始数据集路径
    "target_path":"/home/aistudio/data/dataset",        #要解压的路径 
    "train_list_path": "./train.txt",              #train_data.txt路径
    "eval_list_path": "./eval.txt",                  #eval_data.txt路径
    "label_dict":,                                    #标签字典
    "readme_path": "/home/aistudio/data/readme.json",   #readme.json路径
    "num_epochs": 40,                                    #训练轮数
    "train_batch_size": 32,                             #批次的大小
    "learning_strategy":                               #优化函数相关的配置
        "lr": 0.0001                                     #超参数学习率
     

  1. 定义解压函数,解压数据集:
def unzip_data(src_path,target_path):

    '''
    解压原始数据集,将src_path路径下的zip包解压至data/dataset目录下
    '''

    if(not os.path.isdir(target_path)):    
        z = zipfile.ZipFile(src_path, 'r')
        z.extractall(path=target_path)
        z.close()
    else:
        print("文件已解压")

  1. 生成数据列表:读取每个文件夹下的图片,将绝对路径统一保存于文件中:
def get_data_list(target_path,train_list_path,eval_list_path):
    '''
    生成数据列表
    '''
    #存放所有类别的信息
    class_detail = []
    #获取所有类别保存的文件夹名称
    data_list_path=target_path
    class_dirs = os.listdir(data_list_path)
    if '__MACOSX' in class_dirs:
        class_dirs.remove('__MACOSX')
    # #总的图像数量
    all_class_images = 0
    # #存放类别标签
    class_label=0
    # #存放类别数目
    class_dim = 0
    # #存储要写进eval.txt和train.txt中的内容
    trainer_list=[]
    eval_list=[]
    #读取每个类别
    for class_dir in class_dirs:
        if class_dir != ".DS_Store":
            class_dim += 1
            #每个类别的信息
            class_detail_list = 
            eval_sum = 0
            trainer_sum = 0
            #统计每个类别有多少张图片
            class_sum = 0
            #获取类别路径 
            path = os.path.join(data_list_path,class_dir)
            # 获取所有图片
            img_paths = os.listdir(path)
            for img_path in img_paths:                                  # 遍历文件夹下的每个图片
                if img_path =='.DS_Store':
                    continue
                name_path = os.path.join(path,img_path)                       # 每张图片的路径
                if class_sum % 15 == 0:                                 # 每10张图片取一个做验证数据
                    eval_sum += 1                                       # eval_sum为测试数据的数目
                    eval_list.append(name_path + "\\t%d" % class_label + "\\n")
                else:
                    trainer_sum += 1 
                    trainer_list.append(name_path + "\\t%d" % class_label + "\\n")#trainer_sum测试数据的数目
                class_sum += 1                                          #每类图片的数目
                all_class_images += 1                                   #所有类图片的数目
            
            # 说明的json文件的class_detail数据
            class_detail_list['class_name'] = class_dir             #类别名称
            class_detail_list['class_label'] = class_label          #类别标签
            class_detail_list['class_eval_images'] = eval_sum       #该类数据的测试集数目
            class_detail_list['class_trainer_images'] = trainer_sum #该类数据的训练集数目
            class_detail.append(class_detail_list)  
            #初始化标签列表
            train_parameters['label_dict'][str(class_label)] = class_dir
            class_label += 1
            
    #初始化分类数
    train_parameters['class_dim'] = class_dim
    print(train_parameters)
    #乱序  
    random.shuffle(eval_list)
    with open(eval_list_path, 'a') as f:
        for eval_image in eval_list:
            f.write(eval_image) 
    #乱序        
    random.shuffle(trainer_list) 
    with open(train_list_path, 'a') as f2:
        for train_image in trainer_list:
            f2.write(train_image) 

    # 说明的json文件信息
    readjson = 
    readjson['all_class_name'] = data_list_path                  #文件父目录
    readjson['all_class_images'] = all_class_images
    readjson['class_detail'] = class_detail
    jsons = json.dumps(readjson, sort_keys=True, indent=4, separators=(',', ': '))
    with open(train_parameters['readme_path'],'w') as f:
        f.write(jsons)
    print ('生成数据列表完成!')
  1. 调用前面的功能函数,生成数据列表,用于后面的训练与验证:
'''
参数初始化
'''
src_path=train_parameters['src_path']
target_path=train_parameters['target_path']
train_list_path=train_parameters['train_list_path']
eval_list_path=train_parameters['eval_list_path']
batch_size=train_parameters['train_batch_size']
'''
解压原始数据到指定路径
'''
unzip_data(src_path,target_path)

'''
划分训练集与验证集,乱序,生成数据列表
'''
#每次生成数据列表前,首先清空train.txt和eval.txt
with open(train_list_path, 'w') as f: 
    f.seek(0)
    f.truncate() 
with open(eval_list_path, 'w') as f: 
    f.seek(0)
    f.truncate() 
    
#生成数据列表   
get_data_list(target_path,train_list_path,eval_list_path)
  1. 为训练模型,需要定义一个数据集类将数据进行封装,该类需要继承paddle.io.Dataset抽象类,Dataset抽象了数据集的方法和行为,须实现以下方法:

    __ getitem__:根据给定索引获取数据集中指定样本,在paddle.io.DataLoader中需要使用此函数通过下标获取样本;

    __ len__:返回数据集样本个数,paddle.io.BatchSampler中需要样本个数生成下标序列。

本实验中自定义Reader(命名可自定义)类继承Dataset,然后再使用paddle.io.DataLoader进行批量数据处理,获取可批量迭代的数据加载器:

class Reader(Dataset):
    def __init__(self, data_path, mode='train'):
        """
        数据读取器
        :param data_path: 数据集所在路径
        :param mode: train or eval
        """
        super().__init__()
        self.data_path = data_path
        self.img_paths = []
        self.labels = []

        if mode == 'train':
            with open(os.path.join(self.data_path, "train.txt"), "r", encoding="utf-8") as f:
                self.info = f.readlines()
            for img_info in self.info:
                img_path, label = img_info.strip().split('\\t')
                self.img_paths.append(img_path)
                self.labels.append(int(label))

        else:
            with open(os.path.join(self.data_path, "eval.txt"), "r", encoding="utf-8") as f:
                self.info = f.readlines()
            for img_info in self.info:
                img_path, label = img_info.strip().split('\\t')
                self.img_paths.append(img_path)
                self.labels.append(int(label))


    def __getitem__(self, index):
        """
        获取一组数据
        :param index: 文件索引号
        :return:
        """
        # 第一步打开图像文件并获取label值
        img_path = self.img_paths[index]
        img = Image.open(img_path)
        if img.mode != 'RGB':
            img = img.convert('RGB') 
        img = img.resize((224, 人工智能需要啥基础?

高清图解:神经网络机器学习数据科学一网打尽

零基础机器学习做游戏辅助第一课--神经网络与Keras介绍

鲲鹏云实验-Python+Jupyter机器学习基础环境

BP神经网络-手写数字的识别-机器学习实验二

机器学习-基于Sklearn的神经网络实现