您的当前位置:首页正文

机器学习实践:非监督学习的自编码器-9

2024-11-29 来源:个人技术集锦

机器学习实践:非监督学习自编码器

1、实验描述

  • 利用非监督学习的自编码器,实现数据可视化降维

  • 实验时长:90分钟

  • 主要步骤:

    • 数据准备

    • 前向计算

    • 误差反向传播

    • 自编码器三层神经网络训练所需的参数

    • 结果展示

2、实验环境

  • 虚拟机数量:1

  • 系统版本:CentOS 7.5

  • Python版本: 2.7

  • Scipy版本:1.1.0

  • Numpy版本:1.15.1

  • Matplotlib版本:2.2.3

3、相关技能

  • vim文本编辑器
  • Python 依赖包numpy
  • Python依赖包Matplotlib
  • yum依赖包tkinter
  • 神经网络原理

4、相关知识点

  • 自编码器实现
  • 前向传播算法
  • 反向传播算法
  • 归一化
  • 批量梯度下降
  • 随机梯度下降
  • 最小批梯度下降
  • 无监督学习

5、实现效果

  • 下图为自编码器的编码结果

图 1

6、实验步骤

6.1实验数据准备

[zkpk@master ~]$ cp -r experiment/06/  /home/zkpk/
[zkpk@master ~]$ cd 06/
[zkpk@master 06]$ su
[root@master 06]$ yum install tkinter
[root@master 06]$ exit
exit
[zkpk@master 06]$

6.2自编码器实现

6.2.1创建python文件,编写代码unsupervised.py

[zkpk@master 06]$ vim unsupervised.py

6.2.2导入需要的包:

# -*- coding: utf-8 -*-
import scipy.io as scio
import numpy as np
import matplotlib.pyplot as plt
import random

6.2.3导入数据,数据是基于minist手写体数字数据库中的500张图片,每个数字(0-9)都有其对应的50张手写体图片,图片的维度为28 * 28

def data(data):
    trainData = scio.loadmat(data)	# loadmat加载MATLAB文件
    unlabeled_data = trainData['trainData']
    unlabeled_data = unlabeled_data[:, :] / 255.
    return unlabeled_data

6.2.4前向计算代码实现:

#前向计算
# w:权值矩阵 a:神经网络内部节点 x:神经网络外部节点
def feedforward(w, a, x):
    # 激活函数sigmoid
    sig = lambda s: 1 / (1 + np.exp(-s))	# exp方法返回e的幂次方

    # 将网络的内部及外部输入联合起来与权值矩阵进行加权叠加
    # 这里使用的是矩阵运算使训练更加快捷
    w = np.array(w)
    temp = np.array(np.concatenate((a, x), axis=0))	# concatenate用来拼接两个数组
    rs = np.dot(w, temp)	# 若dot的两个参数是一维数组,则得到两数组的内积;若两个参数是二维数组,结果是矩阵积

    # 返回计算的下一层神经元的计算结果
    # 及未经过激活函数前的加权叠加结果
    return sig(rs), rs

6.2.5误差反向传播代码实现:

#误差反向传播
# w: 权值矩阵 z: 当前层的未经过激活函数前的神经元值
# delta_next: 下一层的 δ
def backprop(w, z, delta_next):
    # sigmoid 激活函数
    sig = lambda s: np.array(1 / (1 + np.exp(-s)))

    # 激活函数 sigmoid 的导数
    df = lambda s: sig(s) * (1 - sig(s))

    # 误差反向传播计算上一层的 δ 并返回
    delta = df(z) * np.dot(w.T, delta_next)

    return delta

6.2.6编写主函数并设置自编码器三层神经网络训练所需的参数:

def main():
    alpha = 5  # 学习步长
    max_epoch = 500  # 训练的最大迭代次数
    mini_batch = 50  # 在线学习批训练一次训练的次数,50次就可以达到比较好的效果,与100次效果差别不大
    imgSize = 28*28  # 每张图片的大小为28,共784个像素
    unlabeled_data=data('trainData.mat')
    # 定义神经网络结构
    # 第一列为外部节点(神经元)
    # 第二列为内部节点,最后一层为网络输出
    layer_struc = [[imgSize, 1],
                   [0, 32],
                   [0, imgSize]]
    layer_num = 3

6.2.7初始化权值矩阵

    # 初始化权值矩阵
    w = []
    for l in range(layer_num - 1):
        w.append(np.random.randn(layer_struc[l + 1][1], sum(layer_struc[l])))
    dataset_size = 500  # 训练数据集大小

6.2.8定义神经网络的外部输入

    # 定义神经网络的外部输入
    # 虽然只有第一层外部输入
    # 但是为了训练时代码的统一,也设置了最后两层空的外部输入
    X = []
    X.append(np.array(unlabeled_data[:, :]))
    X.append(np.zeros((0, dataset_size)))
    X.append(np.zeros((0, dataset_size)))

6.2.9初始化误差反向传播

    #初始化误差反向传播
    delta = []
    for l in range(layer_num):
        delta.append([])

6.2.10定义结果展示的参数

    # 定义结果展示的参数
    # 每隔100个epoch展示一次自编码结果
    # 加上第一行的原始图片展示
    nRow = max_epoch / 100 + 1
    nColumn = 10  # 每一行展示0-9十个数字
    trainimage = 50 # 每个数字都有50张训练图片

6.2.11自编码器的训练

    #自编码器的训练,及结果展示
    for iImg in range(nColumn):
        ax = plt.subplot(nRow, nColumn, iImg + 1)
        plt.imshow(unlabeled_data[:, trainimage * iImg + 1].reshape((28, 28)).T, cmap=plt.cm.gray)

        ax.get_xaxis().set_visible(False)
        ax.get_yaxis().set_visible(False)
    count = 0  # 迭代次数计步器
    print('Autoencoder training start..')
    for iter in range(max_epoch):

        # 定义shuffle时的下标
        ind = list(range(dataset_size))
        # 为每次训练时都将训练数据重新打乱一遍
        random.shuffle(ind)

        a = []  # 网络内部神经元
        z = []  # 网络节点的加权叠加结果
        z.append([])

        # 对神经网络开始批训练
        for i in range(int(np.ceil(dataset_size / mini_batch))):
            a.append(np.zeros((layer_struc[0][1], mini_batch)))
            x = []
            for l in range(layer_num):
                x.append(X[l][:, ind[i * mini_batch: min((i + 1) * mini_batch, dataset_size)]])

            # 定义目标输出
            y = unlabeled_data[:, ind[i * mini_batch:min((i + 1) * mini_batch, dataset_size)]]

            # 调用前向计算函数计算网络每一层节点的值
            for l in range(layer_num - 1):
                a.append([])
                z.append([])
                a[l + 1], z[l + 1] = feedforward(w[l], a[l], x[l])

            # 根据最小二乘代价函数计算最后一层的δ
            # 即自编码器的实际输出与目标值的欧式距离
            delta[layer_num - 1] = np.array(a[layer_num - 1] - y) * np.array(a[layer_num - 1])
            delta[layer_num - 1] = delta[layer_num - 1] * np.array(1 - a[layer_num - 1])

            # 误差反向传播过程
            # 调用backprop函数逐层反向计算 δ 值
            for l in range(layer_num - 2, 0, -1):
                delta[l] = backprop(w[l], z[l], delta[l + 1])

            for l in range(layer_num - 1):
                dw = np.dot(delta[l + 1], np.concatenate((a[l], x[l]), axis=0).T) / mini_batch
                w[l] = w[l] - alpha * dw

        count = count + 1

        # 展示自编码器编码结果
        if np.mod(iter + 1, 100) == 0:
            b = []
            b.append(np.zeros((layer_struc[0][1], dataset_size)))

            for l in range(layer_num - 1):
                tempA, tempZ = feedforward(w[l], b[l], X[l])
                b.append(tempA)

            for iImg in range(nColumn):
                ax = plt.subplot(nRow, nColumn, iImg + nColumn * (iter + 1) / 100 + 1)
                dis_result = b[layer_num - 1][:, trainimage * iImg + 1].reshape(28, 28).T
                plt.imshow(dis_result, cmap=plt.cm.gray)
                ax.get_xaxis().set_visible(False)
                ax.get_yaxis().set_visible(False)

            print('Learning epoch:', count, '/', max_epoch)
    plt.show()
if __name__ == '__main__':
    main()

6.3运行代码,展示结果

[zkpk@master 06]$ python unsupervised.py

图 2
![在这里插入图片描述](https://img-blog.csdnimg.cn/dbab52504a9340ef946f9ce08c2ccdcf.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aWU6IW-5ri45a2Q,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
图 3

7、参考答案

  • 代码详见 unsupervised.py

图 4

图 5
![在这里插入图片描述](https://img-blog.csdnimg.cn/7b84a3746c5045ec8ce7ea63c99299fe.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aWU6IW-5ri45a2Q,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
图 6

图 7
![在这里插入图片描述](https://img-blog.csdnimg.cn/9311f0a6f9ea488bbd9342782815fe01.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5aWU6IW-5ri45a2Q,size_20,color_FFFFFF,t_70,g_se,x_16#pic_center)
图 8

8、总结

本次实验我们简单实现了基于非监督学习的自编码器,通过设置网络参数,可以影响自编码器的性能,训练步长一般是通过运行代码验证选取,并不是越大或者越小越好,只能通过经验选择适当的步长,但是通过增加迭代次数,可以使自编码器的训练结果越来越好,因此我们在设置这些参数的时候,大多数情况都靠经验,也因此会花费大量时间调参以获得更好的结果。

显示全文