【机器学习】深度学习概论(一)

经典的机器学习算法与深度学习对比

aaeb36a3cad0c03780288f5a0c12c921.png

一、机器学习面临的挑战

    0fa8a689ec8e633ecff16d7741e2735a.jpeg

1.1 机器学习算法用于各种应用问题时所利用的典型特征

1b2eb28a01833a0f3fea468493153ad2.jpeg

1.2 采用人工特征的机器学习算法处理流程

da902d8e89a965b9cf3b0605369ed315.jpeg

1.3 人工设计特征面临的问题

f83ed32a9bcf271714a93d7716b446d0.jpeg

二、 深度学习技术

2.1 采用受限玻尔兹曼机和逐层训练的方法训练深层网络

5b6b28fccb8a8aff7a57d5ebfb399d05.jpeg

2.2 自动编码器

e86ee722fe811325af388787dee9e571.jpeg

2.3 训练有多个隐含层的自动编码器存在困难

85b8bc81906e4ae761e7830de94a56ac.jpeg

2.4 AlexNet 网络

bbd7178822dff0b2483246c35582853b.jpeg

2.5 循环神经网络(Recurrent Neural Network,RNN)

568f91b0ac677bccf1279c95ec7facde.jpeg

2.6 解决深层神经网络梯度消失和退化以及局部最优解等问题

4674a03a816ffac33a72459b0b9ea957.jpeg

三、进展和典型应用

深度学习技术在机器视觉领域、语音识别、自然语言处理、数据挖掘、推荐系统、计算机图形学等方向的应用

9a66520f9d2e886f0aa03dac15ce9004.png

3.1 计算机视觉

3b16ca6c90fbad7955a365989ceaa2b5.jpeg

0caa5fff3f75d3ebb8ceebae46b66af8.jpeg

664fb5bd930eb774a36fbdf2ba880156.jpeg

3.2 语音识别

c8a58a1e365061060b35e52a70465926.jpeg

3.3 自然语言处理

0b49296f184d0b2d52e22e91f1bfebeb.jpeg

3.4 计算机图形学

2a64e40274eb1cc59602cceafa392aec.jpeg

3.5 推荐系统

e043241c982c9983871703420a15ecb6.jpeg

3.6 深度强化学习

2e902d5e5c267d518064006fecd8a221.jpeg

e565219a0ed480e2dab3e70f50b3ac65.jpeg

四、自动编码器(Autoencoder)

115c1288da3a39c935f756161e73b6b6.jpeg

4.1 自动编码器简介

acdcf12cd2f5661610a660eb3ca74dc7.png

413498ccc91c351c06fdc4f0df2fc8d9.png

示例代码:用TensorFlow实现的一个简单的自动编码器模型,用于对MNIST数据集进行降维和可视化。

# 导入所需的库
import numpy as np # 用于科学计算
import matplotlib.pyplot as plt # 用于绘图
from tensorflow.keras.datasets import mnist # 用于加载MNIST数据集
from tensorflow.keras.models import Model # 用于构建模型
from tensorflow.keras.layers import Input, Dense # 用于定义层


# 加载MNIST数据集,它包含了6万张训练图像和1万张测试图像,每张图像是28*28的灰度图
#x_train是一个形状为(60000, 28, 28)的数组,表示有6万张训练图像,每张图像有28*28个像素值。y_train是一个形状为(60000,)的数组,表示有6万个训练标签,每个标签是一个0到9的整数。x_test和y_test的含义类似,只是它们的数量是1万而已。
(x_train, y_train), (x_test, y_test) = mnist.load_data()
# 将图像数据归一化到[0,1]区间,这样可以加快模型的收敛速度
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0
# 将图像数据展平成一维向量,方便输入到全连接层
#-1表示自动计算该维度的大小,2828表示将每张图像展平成一个784维的向量
x_train = x_train.reshape(-1, 28*28)
x_test = x_test.reshape(-1, 28*28)


# 定义编码器的输入层和输出层,输入层的形状是(28*28,),输出层的形状是(2,),表示将784维的数据降维到2维,输出层使用了ReLU激活函数
input_img = Input(shape=(28*28,))
encoded = Dense(2, activation='relu')(input_img)
# 定义解码器的输入层和输出层,输入层的形状是(2,),输出层的形状是(28*28,),表示将2维的数据还原到784维,输出层使用了Sigmoid激活函数,使得输出值在[0,1]区间
decoded = Dense(28*28, activation='sigmoid')(encoded)


# 构建自动编码器模型,它由编码器和解码器组成,输入是图像数据,输出是重构后的图像数据
autoencoder = Model(input_img, decoded)
# 构建编码器模型,它只包含编码器部分,输入是图像数据,输出是编码后的数据
encoder = Model(input_img, encoded)
# 构建解码器模型,它只包含解码器部分,输入是编码后的数据,输出是重构后的图像数据
decoder_input = Input(shape=(2,))#解码器的输入层,它的形状是(2,),表示输入的数据是2维的向量,这是编码器的输出层的形状
decoder_layer = autoencoder.layers[-1] #获取了自动编码器模型的最后一层,它是一个全连接层,它的形状是(28*28,),表示输出的数据是784维的向量,这是原始图像数据的形状
decoder = Model(decoder_input, decoder_layer(decoder_input)) #构建了解码器模型,它的输入是解码器的输入层,它的输出是自动编码器的最后一层对输入层的计算结果。


# 编译自动编码器模型,使用Adam优化器和二元交叉熵损失函数
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
# 训练自动编码器模型,使用训练数据作为输入和输出,设置10个周期,每个批次256个样本,打乱数据顺序,使用测试数据作为验证数据
autoencoder.fit(x_train, x_train, epochs=10, batch_size=256, shuffle=True, validation_data=(x_test, x_test))


# 用编码器模型对测试数据进行编码,得到编码后的数据,它的形状是(10000, 2),表示有1万个样本,每个样本有2个特征
encoded_imgs = encoder.predict(x_test)
# 用解码器模型对编码后的数据进行解码,得到重构后的图像数据,它的形状是(10000, 28*28),表示有1万个样本,每个样本有784个像素值
decoded_imgs = decoder.predict(encoded_imgs)


# 绘制原始图像和重构图像,比较它们的相似度
n = 10 # 显示的图像数量
plt.figure(figsize=(20, 4))# 大小为20英寸宽,4英寸高
for i in range(n):
    # 显示原始图像,它是28*28的灰度图
    ax = plt.subplot(2, n, i + 1)#第一行第i+1列
    plt.imshow(x_test[i].reshape(28, 28))
    plt.gray()#设置图像的颜色为灰度
    ax.get_xaxis().set_visible(False)#隐藏子图的x轴
    ax.get_yaxis().set_visible(False)


    # 显示重构图像,它是28*28的灰度图
    ax = plt.subplot(2, n, i + 1 + n)# 第二行第i+1列
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
plt.show()


# 绘制编码后的数据的散点图,用不同的颜色表示不同的类别,观察它们的分布情况
plt.figure(figsize=(8, 8))
#绘制散点图,横坐标是编码后的数据的第一维,纵坐标是编码后的数据的第二维,颜色是测试数据的标签,颜色映射是彩虹色
plt.scatter(encoded_imgs[:, 0], encoded_imgs[:, 1], c=y_test, cmap='rainbow')
plt.colorbar()#添加一个颜色条,显示不同的颜色对应的数字类别
plt.xlabel('Dimension 1')#设置横坐标的标签
plt.ylabel('Dimension 2')
plt.show()

输出结果:

07c3acacd05c7519781192d78481affe.png

测试集中前10个原始图像和重建图像

3080472c59e28a0defa48b446799c23c.png

4.2 去噪编码器(Denoising Autoencoder)

5be9d121ad0e5b3e24804b55df8500d4.png

示例代码:

#去噪自编码器是一种神经网络,它可以从带有噪声的图像中恢复出原始的清晰图像。
# 用带噪声的图像作为输入,用原始的图像作为输出,让网络学习如何去除噪声
# 导入keras的相关模块
import os
from keras.layers import Input, Conv2D, MaxPooling2D, UpSampling2D
from keras.models import Model
from keras.callbacks import TensorBoard
from keras.datasets import mnist
# 导入numpy和matplotlib的相关模块
import numpy as np
import matplotlib.pyplot as plt


# 加载mnist数据集,只使用图像数据,不使用标签数据
(x_train, _), (x_test, _) = mnist.load_data()
# 将图像数据转换为浮点型,并归一化到[0,1]区间
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
# 将图像数据调整为四维张量,第一维是样本数,后三维是图像的高、宽、通道数
# 一个四维的numpy数组,存储了训练集的图像数据,每个图像的形状为(28, 28, 1)
x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
x_test = np.reshape(x_test, (len(x_test), 28, 28, 1))


# 定义噪声因子,用于在图像数据中添加随机噪声
noise_factor = 0.5
# 在训练集和测试集中添加正态分布的随机噪声
# np.random.normal(loc=0.0, scale=1.0, size=x_train.shape)是一个numpy函数,用于生成一个与x_train形状相同的随机数组,每个元素都服从均值为0,标准差为1的正态分布
x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape)
x_test_noisy = x_test + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape)
# 将噪声数据裁剪到[0,1]区间 添加噪声后,图像的像素值可能会超出 0 到 1 的范围,这会影响网络的性能。
# 所以,我们需要用 np.clip 函数来将像素值限制在 0 到 1 之间,保证输入的合法性。
x_train_noisy = np.clip(x_train_noisy, 0., 1.)
x_test_noisy = np.clip(x_test_noisy, 0., 1.)


# 定义要显示的图像数量
n = 10
# 创建一个大小为(20, 2)的图形窗口
plt.figure(figsize=(20, 2))
# 循环显示测试集中的噪声图像
for i in range(n):
    # 创建一个子图,位置为第一行第i+1列
    ax = plt.subplot(1, n, i + 1)
    # 显示第i个噪声图像,将其调整为28*28的灰度图
    plt.imshow(x_test_noisy[i].reshape(28, 28))
    plt.gray()
    # 隐藏子图的坐标轴
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
# 显示图形窗口
plt.show()


# 定义输入图像的形状,为(28, 28, 1)
input_img = Input(shape=(28, 28, 1))


# 定义编码器部分,使用卷积层和最大池化层实现特征提取和降维
x = Conv2D(32, (3, 3), activation='relu', padding='same')(input_img)    # (28, 28, 32)
x = MaxPooling2D((2, 2), padding='same')(x)                             # (14, 14, 32)
x = Conv2D(32, (3, 3), activation='relu', padding='same')(x)            # (14, 14, 32)
encoded = MaxPooling2D((2, 2), padding='same')(x)                       # (7, 7, 32)


# 定义解码器部分,使用卷积层和上采样层实现特征还原和升维
x = Conv2D(32, (3, 3), activation='relu', padding='same')(encoded)      # (7, 7, 32)
x = UpSampling2D((2, 2))(x)                                             # (14, 14, 32)
x = Conv2D(32, (3, 3), activation='relu', padding='same')(x)            # (14, 14, 32)
x = UpSampling2D((2, 2))(x)                                             # (28, 28, 32)
# 使用卷积层输出重建的图像,激活函数为sigmoid,保证输出值在[0,1]区间
decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)


# 定义自动编码器模型,输入为噪声图像,输出为重建图像
autoencoder = Model(input_img, decoded)
# 编译自动编码器模型,优化器为adadelta,损失函数为二元交叉熵
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')


logdir = os.path.join(os.getcwd(), 'my_logs')
# 训练自动编码器模型,输入为噪声图像,输出为原始图像
autoencoder.fit(x_train_noisy, x_train,
                epochs=100, # 迭代次数为100
                batch_size=128, # 批次大小为128
                shuffle=True, # 每次迭代前打乱数据
                validation_data=(x_test_noisy, x_test), # 使用测试集作为验证集
                callbacks=[TensorBoard(log_dir=logdir, histogram_freq=0, write_graph=False)]) # 使用TensorBoard回调函数记录训练过程


# 使用自动编码器模型对测试集中的噪声图像进行预测,得到重建图像
decoded_imgs = autoencoder.predict(x_test_noisy)


# 定义要显示的图像数量
n = 10
# 创建一个大小为(20, 4)的图形窗口
plt.figure(figsize=(20, 4))
# 循环显示测试集中的噪声图像和重建图像
for i in range(n):
    # 显示噪声图像,位置为第一行第i+1列
    ax = plt.subplot(2, n, i + 1)
    plt.imshow(x_test_noisy[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)


    # 显示重建图像,位置为第二行第i+1列
    ax = plt.subplot(2, n, i + 1 + n)
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    plt.gray()
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
# 显示图形窗口
plt.show()

e8316971905300ab87d15e194fed9e70.png

测试集中前10个噪声图像

19966bf34a5b836c3d7a5383681d2d4e.png

测试集中前10噪声图像和重建图像

4.3 稀疏自动编码器(Sparse Autoencoder)

47ea756b1f7ffc2616beb17922ae4fd2.jpeg

示例代码(pytorch):

# 导入 torch 模块,用于构建和训练神经网络
import torch
# 导入 torch.nn 模块,用于定义神经网络的层和损失函数
import torch.nn as nn
# 导入 torch.nn.functional 模块,用于实现一些常用的激活函数和其他函数
import torch.nn.functional
# 导入 torch.optim 模块,用于实现优化算法
import torch.optim as optim
# 导入 torch.utils.data.dataloader 模块,用于加载和处理数据
import torch.utils.data.dataloader as dataloader


# 导入 torchvision 模块,用于处理图像数据
import torchvision
# 导入 torchvision.datasets 模块,用于获取一些常用的数据集
import torchvision.datasets as datasets
# 导入 torchvision.transforms 模块,用于对图像数据进行一些变换
import torchvision.transforms as transforms


# 导入 common.datas 模块,用于获取 MNIST 数据集的加载器
from common.datas import get_mnist_loader


# 导入 os 模块,用于操作系统相关的功能
import os
# 导入 time 模块,用于获取时间相关的信息
import time
# 导入 matplotlib.pyplot 模块,用于绘制图形
import matplotlib.pyplot as plt
# 导入 PIL.Image 模块,用于处理图像
from PIL import Image


# 定义一些超参数
batch_size = 100 # 批次大小,即每次训练的数据量
num_epochs = 50 # 训练的轮数,即所有数据训练的次数
in_dim = 784 # 输入维度,即图像的像素数,28*28=784
hidden_size = 30 # 隐藏层维度,即隐藏层神经元的个数
expect_tho = 0.05 # 期望的平均激活值,用于稀疏性约束




def KL_devergence(p, q):
    """
    计算两个分布的 KL 散度
    :param p: 期望的分布
    :param q: 实际的分布
    :return: KL 散度
    """
    # 编码器激活函数是relu,输出没有限制在0~1
    # 对 q 这个张量进行 softmax 函数的运算,使得 q 的每个元素缩放到 (0, 1) 区间且和为 1
    q = torch.nn.functional.softmax(q, dim=0) # 对 q 进行 softmax 归一化,使其和为 1
    q = torch.sum(q, dim=0)/batch_size  # 对 q 的第一维求和,即将第 j 个神经元在 batch_size 个输入下的所有输出取平均
    s1 = torch.sum(p*torch.log(p/q)) # 计算 p 和 q 的交叉熵
    s2 = torch.sum((1-p)*torch.log((1-p)/(1-q))) # 计算 1-p 和 1-q 的交叉熵
    return s1+s2 # 返回 KL 散度


# 定义一个自编码器类,继承自 nn.Module
class AutoEncoder(nn.Module): 
    def __init__(self, in_dim=784, hidden_size=30, out_dim=784): # 定义类的初始化方法,接受输入维度、隐藏层维度和输出维度作为参数
        super(AutoEncoder, self).__init__() # 调用父类的初始化方法
        self.encoder = nn.Sequential( # 定义编码器,即将输入数据压缩为隐藏层表示的部分
            nn.Linear(in_features=in_dim, out_features=hidden_size), # 定义一个全连接层,将输入维度映射为隐藏层维度
            nn.ReLU() # 定义一个 ReLU 激活函数,增加非线性
        )
        self.decoder = nn.Sequential( # 定义解码器,即将隐藏层表示恢复为输出数据的部分
            nn.Linear(in_features=hidden_size, out_features=out_dim), # 定义一个全连接层,将隐藏层维度映射为输出维度
            nn.Sigmoid() # 定义一个 Sigmoid 激活函数,将输出限制在 0 到 1 之间,因为图像的像素值在 0 到 1 之间
        )


    def forward(self, x): # 定义类的前向传播方法,接受输入数据 x 作为参数
        encoder_out = self.encoder(x) # 调用编码器,得到隐藏层表示
        decoder_out = self.decoder(encoder_out) # 调用解码器,得到输出数据
        return encoder_out, decoder_out # 返回隐藏层表示和输出数据




train_loader, test_loader = get_mnist_loader(batch_size=batch_size, shuffle=True) # 调用 get_mnist_loader 函数,获取 MNIST 数据集的训练集和测试集的加载器
autoEncoder = AutoEncoder(in_dim=in_dim, hidden_size=hidden_size, out_dim=in_dim) # 创建一个自编码器对象,传入输入维度、隐藏层维度和输出维度作为参数
if torch.cuda.is_available(): # 判断是否有 GPU 可用
    autoEncoder.cuda()  # 将模型放到 GPU 上,因此后续传入的数据必须也在 GPU 上


Loss = nn.BCELoss() # 定义损失函数,使用二元交叉熵损失,用于衡量输出数据和输入数据的差异
Optimizer = optim.Adam(autoEncoder.parameters(), lr=0.001) # 定义优化器,使用 Adam 算法,传入自编码器的参数和学习率作为参数


# 定义期望平均激活值和 KL 散度的权重
#用于计算隐藏层神经元的平均激活值和期望的平均激活值之间的 KL 散度,从而增加稀疏性的约束。
# 期望的平均激活值是一个很小的值,比如 0.05,表示我们希望隐藏层神经元的激活值的平均值接近于这个值,
# 这样可以使得隐藏层神经元只有少数的激活,而大多数的抑制,从而提取输入数据的重要特征
tho_tensor = torch.FloatTensor([expect_tho for _ in range(hidden_size)]) # 创建一个张量,存储期望的平均激活值,大小为隐藏层维度
if torch.cuda.is_available(): # 判断是否有 GPU 可用
    tho_tensor = tho_tensor.cuda() # 将张量放到 GPU 上
_beta = 3 # 定义 KL 散度的权重,用于控制稀疏性的程度


# def kl_1(p, q):
#     p = torch.nn.functional.softmax(p, dim=-1)
#     _kl = torch.sum(p*(torch.log_softmax(p,dim=-1)) - torch.nn.functional.log_softmax(q, dim=-1),1)
#     return torch.mean(_kl)


for epoch in range(num_epochs): # 对所有数据进行 num_epochs 轮训练
    time_epoch_start = time.time() # 记录每轮训练的开始时间
    #enumerate 是一个内置函数,它可以将一个可迭代的对象转换为一个枚举对象,即在每个元素前面加上一个计数值,从 0 开始。这样可以方便地获取每个元素的索引和值。
    # MNIST 数据集 train_data 的数据维度应该是 (batch_size, 1, 28, 28),其中 batch_size 是您设置的每次训练的数据量,1 是图像的通道数,28 是图像的高度和宽度
    for batch_index, (train_data, train_label) in enumerate(train_loader): # 对训练集的每个批次进行迭代,获取批次索引、数据和标签
        if torch.cuda.is_available(): # 判断是否有 GPU 可用
            train_data = train_data.cuda() # 将数据放到 GPU 上
            train_label = train_label.cuda() # 将标签放到 GPU 上
        input_data = train_data.view(train_data.size(0), -1) # 将 train_data 的每个图像数据转换为一个一维的向量,大小为 784 
        # train_data.size(0) 是一个整数,表示 train_data 的第一维的大小,即批次大小,即每次训练的数据量
        # view 是一个 torch 模块提供的函数,用于改变张量的形状,即维度和大小
        # input_data 的形状应该是 (batch_size, 784),其中 batch_size 是 train_data 的第一维的大小,784 是 train_data 的其他三维的乘积,即 1×28×28
        encoder_out, decoder_out = autoEncoder(input_data) # 调用自编码器的前向传播方法,得到隐藏层表示和输出数据
        loss = Loss(decoder_out, input_data) # 计算损失函数,比较输出数据和输入数据的差异


        # 计算并增加 KL 散度到损失
        _kl = KL_devergence(tho_tensor, encoder_out) # 调用 KL_devergence 函数,计算期望的分布和实际的分布的 KL 散度
        loss += _beta * _kl # 将 KL 散度乘以权重后加到损失上,增加稀疏性的约束


        Optimizer.zero_grad() # 清空优化器的梯度
        loss.backward() # 调用损失的反向传播方法,计算梯度
        Optimizer.step() # 调用优化器的更新

最后几行输出:

Epoch: 50, Loss: 3.8174, Time: 6.95
Epoch: 50, Loss: 3.8174, Time: 6.97
Epoch: 50, Loss: 3.8171, Time: 6.98
Epoch: 50, Loss: 3.8215, Time: 6.99
Epoch: 50, Loss: 3.8229, Time: 7.00

4.4 收缩自动编码器(Contractive Autoencoder )

84b74114bdc9f8a7330c6103ddb4ac74.png

示例代码(pytorch)

# 导入所需的库
import os
import argparse
import torch
import torch.utils.data
import torch.nn as nn
import torch.optim as optim
from torch.autograd import Variable
from torchvision import datasets, transforms
# import pdb
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec


# 设置CUDA设备的顺序和可见性
os.environ["CUDA_DEVICE_ORDER"]="PCI_BUS_ID"   # see issue #152
os.environ["CUDA_VISIBLE_DEVICES"]="1"


# 打印导入成功的信息
print("Imported all libraries successfully!")


# 创建一个解析器对象,用于处理命令行参数
parser = argparse.ArgumentParser(description='PyTorch MNIST Example for CAE')
# 添加各种参数,包括批量大小,训练轮数,是否使用CUDA,随机种子,日志间隔等
parser.add_argument('--batch-size', type=int, default=128, metavar='N',
                    help='input batch size for training (default: 64)')
parser.add_argument('--epochs', type=int, default=19, metavar='N',
                    help='number of epochs to train (default: 2)')
parser.add_argument('--no-cuda', action='store_true', default=False,
                    help='enables CUDA training')
parser.add_argument('--seed', type=int, default=1, metavar='S',
                    help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
                    help='how many batches to wait before logging training status')
# 解析参数并赋值给args对象
args = parser.parse_args()
# 判断是否使用CUDA
args.cuda = not args.no_cuda and torch.cuda.is_available()


# 设置随机种子
torch.manual_seed(args.seed)
# 如果使用CUDA,设置CUDA的随机种子
if args.cuda:
    torch.cuda.manual_seed(args.seed)


# 设置数据加载器的参数,如果使用CUDA,设置num_workers和pin_memory
kwargs = {'num_workers': 5, 'pin_memory': True} if args.cuda else {}


# 创建训练数据加载器,使用MNIST数据集,将图片转换为张量
train_loader = torch.utils.data.DataLoader(
  datasets.MNIST('data', train=True, download=True,
    transform=transforms.ToTensor()),
  batch_size=args.batch_size, shuffle=True, **kwargs)


# 创建测试数据加载器,使用MNIST数据集,将图片转换为张量
test_loader = torch.utils.data.DataLoader(
    datasets.MNIST('data', train=False, transform=transforms.ToTensor()),
    batch_size=args.batch_size, shuffle=True, **kwargs)


# 设置收缩损失的系数
lam = 1e-4


# 定义一个收缩自编码器(CAE)的类,继承自nn.Module
class CAE(nn.Module):
  def __init__(self):
    super(CAE, self).__init__()


    self.fc1 = nn.Linear(784, 400, bias = False) # 编码器
    self.fc2 = nn.Linear(400, 784, bias = False) # 解码器


    self.relu = nn.ReLU()
    self.sigmoid = nn.Sigmoid()




  def encoder(self, x):
    h1 = self.relu(self.fc1(x.view(-1, 784))) # 将输入图片展平为784维向量,然后通过全连接层和激活函数得到400维的隐层向量
    return h1


  def decoder(self,z):
    h2 = self.sigmoid(self.fc2(z)) # 将隐层向量通过全连接层和激活函数得到784维的重构向量
    return h2


  def forward(self, x):
            h1 = self.encoder(x) # 编码过程
            h2 = self.decoder(h1) # 解码过程
            return h1, h2 # 返回隐层向量和重构向量


        # 将重构的图片按网格排列并保存,用于检查质量和进度
  def samples_write(self, x, epoch):
    _, samples = self.forward(x) # 得到重构向量
    #pdb.set_trace()
    samples = samples.data.cpu().numpy()[:16] # 将重构向量转换为numpy数组,并取前16个
    fig = plt.figure(figsize=(4, 4)) # 创建一个4x4的画布
    gs = gridspec.GridSpec(4, 4) # 创建一个4x4的网格
    gs.update(wspace=0.05, hspace=0.05) # 设置网格间距
    for i, sample in enumerate(samples): # 遍历每个重构向量
      ax = plt.subplot(gs[i]) # 在对应的子图上绘制
      plt.axis('off') # 关闭坐标轴
      ax.set_xticklabels([]) # 设置x轴刻度为空
      ax.set_yticklabels([]) # 设置y轴刻度为空
      ax.set_aspect('equal') # 设置等比例缩放
      plt.imshow(sample.reshape(28, 28), cmap='Greys_r') # 将重构向量还原为28x28的图片,并以灰度显示
    if not os.path.exists('out/'): # 如果输出文件夹不存在,创建一个
      os.makedirs('out/')
    plt.savefig('out/{}.png'.format(str(epoch).zfill(3)), bbox_inches='tight') # 保存图片,文件名为训练轮数,用0补齐
    #self.c += 1
    plt.close(fig) # 关闭画布




# 定义均方误差损失函数,不取平均
mse_loss = nn.BCELoss(size_average = False)


# 定义总损失函数,包括均方误差和收缩损失
def loss_function(W, x, recons_x, h, lam):
    mse = mse_loss(recons_x, x) # 计算重构向量和输入向量的均方误差
    # Since: W is shape of N_hidden x N. So, we do not need to transpose it as
    # opposed to #1
    dh = h * (1 - h) # 计算隐层向量的导数,得到N_batch x N_hidden的矩阵
    # Sum through the input dimension to improve efficiency, as suggested in #1
    w_sum = torch.sum(Variable(W)**2, dim=1) # 计算全连接层的权重矩阵的平方和,得到N_hidden维的向量
    # unsqueeze to avoid issues with torch.mv
    w_sum = w_sum.unsqueeze(1) # 将向量扩展为N_hidden x 1的矩阵
    contractive_loss = torch.sum(torch.mm(dh**2, w_sum), 0) # 计算收缩损失,即隐层向量导数的平方与权重平方和的乘积的和
    return mse + contractive_loss.mul_(lam) # 返回总损失,即均方误差加上收缩损失乘以系数




# 创建一个CAE模型的实例
model = CAE()
# 创建一个优化器,使用Adam算法,学习率为0.0001
optimizer = optim.Adam(model.parameters(), lr = 0.0001)


# 如果使用CUDA,将模型转移到GPU上
if args.cuda:
    model.cuda()


# 定义一个训练函数,接受训练轮数作为参数
def train(epoch):
    model.train() # 将模型设置为训练模式
    train_loss = 0 # 初始化训练损失为0


    # 遍历训练数据加载器,得到每个批次的数据和标签(标签在这里不需要)
    for idx, (data, _) in enumerate(train_loader):
        data = Variable(data) # 将数据转换为变量
        if args.cuda:
            data = data.cuda() # 如果使用CUDA,将数据转移到GPU上


        optimizer.zero_grad() # 清空优化器的梯度缓存


        hidden_representation, recons_x = model(data) # 将数据输入模型,得到隐层向量和重构向量


        # 获取权重矩阵
        # model.state_dict().keys()
        # 根据手动查看的键名修改
        # (将来我会尝试自动化这个过程)
        W = model.state_dict()['fc1.weight'] # 获取编码器的权重矩阵
        loss = loss_function(W, data.view(-1, 784), recons_x,
                             hidden_representation, lam) # 计算总损失函数


        loss.backward() # 反向传播,计算梯度
        train_loss += loss.data[0] # 累加训练损失
        optimizer.step() # 更新参数


        # 如果达到日志间隔,打印训练信息
        if idx % args.log_interval == 0:
            print('Train epoch: {} [{}/{}({:.0f}%)]\t Loss: {:.6f}'.format(
                  epoch, idx*len(data), len(train_loader.dataset),
                  100*idx/len(train_loader),
                  loss.data[0]/len(data)))




    # 打印每轮训练的平均损失
    print('====> Epoch: {} Average loss: {:.4f}'.format(
         epoch, train_loss / len(train_loader.dataset)))
    # 调用模型的方法,将重构的图片保存
    model.samples_write(data,epoch)


# 遍历训练轮数,调用训练函数
for epoch in range(args.epochs):
    train(epoch)

out目录输出:

85d951ddfac3e7dad54d064b3308d4fe.png

4.5 卷积自动编码器(Convolutional Autoencoder)

卷积自编码器(Convolutional Autoencoder)是一种利用卷积神经网络(Convolutional Neural Network, CNN)来实现自编码器功能的深度学习模型,它可以对输入的图像数据进行有效的编码和解码,从而实现图像的降维、去噪、重构等任务。卷积自编码器的结构由两部分组成:卷积编码器(Convolutional Encoder)和卷积解码器(Convolutional Decoder)。卷积编码器使用多个卷积层和池化层(Pooling Layer)来逐渐减小图像的尺寸,提取图像的高级特征,并输出一个压缩的隐层表示。卷积解码器使用多个卷积层和上采样层(Upsampling Layer)来逐渐增大图像的尺寸,恢复图像的细节,并输出一个重构的图像。

示例代码(keras):

# 导入所需的模块和库
from keras.layers import Input, Dense, Conv2D, MaxPooling2D, UpSampling2D # 导入 Keras 中的层模块
from keras.models import Model # 导入 Keras 中的模型模块
from keras.callbacks import TensorBoard # 导入 Keras 中的回调模块,用于可视化训练过程
from keras.datasets import mnist # 导入 Keras 中的数据集模块,用于加载 MNIST 数据集
from keras import backend as K # 导入 Keras 中的后端模块,用于处理张量运算
import numpy as np # 导入 NumPy 库,用于处理数组运算
import matplotlib.pyplot as plt # 导入 Matplotlib 库,用于绘制图像


# 定义输入图像的形状,即 (28, 28, 1),表示高度为 28,宽度为 28,通道数为 1 的灰度图像
input_img = Input(shape=(28, 28, 1))


# 定义编码器部分,即将输入图像压缩为一个低维的向量
x = Conv2D(16, (3, 3), activation='relu', padding='same')(input_img)    # 使用 16 个 3×3 的卷积核对输入图像进行卷积操作,激活函数为 ReLU,填充方式为 same,保持输出图像的大小不变,即 (28, 28, 16)
x = MaxPooling2D((2, 2), padding='same')(x)                             # 使用 2×2 的池化核对卷积后的图像进行最大池化操作,填充方式为 same,将输出图像的大小减半,即 (14, 14, 16)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)             # 使用 8 个 3×3 的卷积核对池化后的图像进行卷积操作,激活函数为 ReLU,填充方式为 same,保持输出图像的大小不变,即 (14, 14, 8)
x = MaxPooling2D((2, 2), padding='same')(x)                             # 使用 2×2 的池化核对卷积后的图像进行最大池化操作,填充方式为 same,将输出图像的大小减半,即 (7, 7, 8)
x = Conv2D(8, (3, 3), activation='relu', padding='same')(x)             # 使用 8 个 3×3 的卷积核对池化后的图像进行卷积操作,激活函数为 ReLU,填充方式为 same,保持输出图像的大小不变,即 (7, 7, 8)
encoded = MaxPooling2D((2, 2), padding='same')(x)                       # 使用 2×2 的池化核对卷积后的图像进行最大池化操作,填充方式为 same,将输出图像的大小减半,即 (4, 4, 8),这就是编码后的向量,共有 128 个元素


# 在这一点上,表示是 (4, 4, 8),即 128 维


# 定义解码器部分,即将编码后的向量还原为原始的输入图像
x = Conv2D(8, (3, 3), activation='relu', padding='same')(encoded)       # 使用 8 个 3×3 的卷积核对编码后的向量进行卷积操作,激活函数为 ReLU,填充方式为 same,保持输出图像的大小不变,即 (4, 4, 8)
x = UpSampling2D((2, 2))(x)                                             # 使用 2×2 的上采样核对卷积后的图像进行上采样操作,将输出图像的大小增加一倍,即 (8, 8, 8)
x = Conv2D(16, (3, 3), activation='relu', padding='same')(x)            # 使用 16 个 3×3 的卷积核对上采样后的图像进行卷积操作,激活函数为 ReLU,填充方式为 same,保持输出图像的大小不变,即 (8, 8, 8)
x = UpSampling2D((2, 2))(x)                                             # 使用 2×2 的上采样核对卷积后的图像进行上采样操作,将输出图像的大小增加一倍,即 (16, 16, 8)
x = Conv2D(16, (3, 3), activation='relu')(x)                            # 使用 16 个 3×3 的卷积核对上采样后的图像进行卷积操作,激活函数为 ReLU,填充方式为 valid,将输出图像的大小减少 2,即 (14, 14, 8)
x = UpSampling2D((2, 2))(x)                                             # 使用 2×2 的上采样核对卷积后的图像进行上采样操作,将输出图像的大小增加一倍,即 (28, 28, 8)
decoded = Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)    # 使用 1 个 3×3 的卷积核对上采样后的图像进行卷积操作,激活函数为 sigmoid,填充方式为 same,保持输出图像的大小不变,即 (28, 28, 1),这就是解码后的图像,与输入图像的形状相同


# 定义自编码器模型,即将输入图像和解码后的图像连接起来
autoencoder = Model(input_img, decoded)


# 编译自编码器模型,使用 adadelta 优化器和loss='binary_crossentropy' 二元交叉熵损失函数
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')


# 定义编码器模型,即将输入图像和编码后的向量连接起来
encoder = Model(input_img, encoded)


# 从 MNIST 数据集中加载训练数据和测试数据,只需要图像数据,不需要标签数据
(x_train, _), (x_test, _) = mnist.load_data()
# 将图像数据转换为浮点类型,并归一化到 [0, 1] 区间
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
# 将图像数据调整为 (28, 28, 1) 的形状,以适应输入图像的形状
x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
x_test = np.reshape(x_test, (len(x_test), 28, 28, 1))


# 训练自编码器模型,使用训练数据作为输入和输出,设置迭代次数为 50,批次大小为 128,打乱数据顺序,使用测试数据作为验证数据,使用 TensorBoard 回调函数来可视化训练过程,将日志文件保存在 conv_autoencoder 目录下
autoencoder.fit(x_train, x_train,
                epochs=50,
                batch_size=128,
                shuffle=True,
                validation_data=(x_test, x_test),
                callbacks=[TensorBoard(log_dir='./conv_autoencoder')])


# 使用编码器模型对测试数据进行编码,得到编码后的向量
encoded_imgs = encoder.predict(x_test)
# 使用自编码器模型对测试数据进行解码,得到解码后的图像
decoded_imgs = autoencoder.predict(x_test)


# 设置要显示的图像的个数,这里为 10 个
n = 10
# 创建一个新的图形窗口,设置大小为 (20, 4)
plt.figure(figsize=(20, 4))
# 循环遍历每个图像
for i in range(n):
    # 显示原始图像
    # 创建一个子图,位置为第 i + 1 个,共有 2 行 n 列
    ax = plt.subplot(2, n, i + 1)
    # 将测试数据中的第 i 个图像从 (28, 28, 1) 的形状还原为 (28, 28) 的形状,并显示出来
    plt.imshow(x_test[i].reshape(28, 28))
    # 设置为灰度模式
    plt.gray()
    # 隐藏 x 轴和 y 轴的刻度
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)


    # 显示重建图像
    # 创建一个子图,位置为第 i + 1 + n 个,共有 2 行 n 列
    ax = plt.subplot(2, n, i + 1 + n)
    # 将解码数据中的第 i 个图像从 (28, 28, 1) 的形状还原为 (28, 28) 的形状,并显示出来
    plt.imshow(decoded_imgs[i].reshape(28, 28))
    # 设置为灰度模式
    plt.gray()
    # 隐藏 x 轴和 y 轴的刻度
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
# 显示图形窗口
plt.show()


# 创建一个新的图形窗口,设置大小为 (20, 8)
plt.figure(figsize=(20, 8))
# 循环遍历每个图像
for i in range(n):
    # 显示编码向量
    # 创建一个子图,位置为第 i + 1 个,共有 1 行 n 列
    ax = plt.subplot(1, n, i + 1)
    # 将编码数据中的第 i 个向量从 (4, 4, 8) 的形状还原为 (4, 32) 的形状,并转置为 (32, 4) 的形状,然后显示出来
    plt.imshow(encoded_imgs[i].reshape(4, 4 * 8).T)
    # 设置为灰度模式
    plt.gray()
    # 隐藏 x 轴和 y 轴的刻度
    ax.get_xaxis().set_visible(False)
    ax.get_yaxis().set_visible(False)
# 显示图形窗口
plt.show()

输出结果:

f6262a89c304a5fbe3e296ad01da33ee.png

13d56e1a5f2c64fca6a66c173b1bb62d.png

4.5 多层编码器-层叠自动编码器(Stacked_AutoEncoder-SAE)

4b1b860610ec184c6e556ba5947fbfc0.png

17a62af97ac540bf11c13e18c14891a5.jpeg

e7e72d2904b455122b6e3afe7abafb40.png

示例代码-SAE 层叠自动编码器(pytorch)

models.py

# 导入所需的库,包括torch,torchvision,time和os
import torch
from torch import nn, optim, functional, utils
import torchvision
from torchvision import datasets, utils


import time, os




# 定义一个自编码器层的类,继承自nn.Module
class AutoEncoderLayer(nn.Module):
    """
    fully-connected linear layers for stacked autoencoders.
    This module can automatically be trained when training each layer is enabled
    Yes, this is much like the simplest auto-encoder
    """


    # 定义初始化方法,接受输入维度,输出维度和是否进行逐层预训练的参数
    def __init__(self, input_dim=None, output_dim=None, SelfTraining=False):
        super(AutoEncoderLayer, self).__init__() # 调用父类的初始化方法
        # if input_dim is None or output_dim is None:
        #     raise ValueError
        self.in_features = input_dim # 保存输入维度
        self.out_features = output_dim # 保存输出维度
        self.is_training_self = SelfTraining  # 指示是否进行逐层预训练,还是训练整个网络
        # 定义编码器,使用全连接层和Sigmoid激活函数
        self.encoder = nn.Sequential(
            nn.Linear(self.in_features, self.out_features, bias=True),
            nn.Sigmoid()  # 统一使用Sigmoid激活
        )
        # 定义解码器,使用全连接层和Sigmoid激活函数
        self.decoder = nn.Sequential(  # 此处decoder不使用encoder的转置, 并使用Sigmoid进行激活.
            nn.Linear(self.out_features, self.in_features, bias=True),
            nn.Sigmoid()
        )


    # 定义前向传播方法,接受输入x
    def forward(self, x):
        out = self.encoder(x) # 将x通过编码器得到隐层向量
        if self.is_training_self: # 如果是逐层预训练,返回解码器的输出
            return self.decoder(out)
        else: # 否则,返回隐层向量
            return out


    # 定义一个方法,锁定该层的梯度,即不更新参数
    def lock_grad(self):
        for param in self.parameters():
            param.requires_grad = False


    # 定义一个方法,解锁该层的梯度,即更新参数
    def acquire_grad(self):
        for param in self.parameters():
            param.requires_grad = True


    # 定义一个属性,返回输入维度
    @property
    def input_dim(self):
        return self.in_features


    # 定义一个属性,返回输出维度
    @property
    def output_dim(self):
        return self.out_features


    # 定义一个属性,返回是否进行逐层预训练的标志
    @property
    def is_training_layer(self):
        return self.is_training_self


    # 定义一个属性的设置方法,接受一个布尔值,设置是否进行逐层预训练的标志
    @is_training_layer.setter
    def is_training_layer(self, other: bool):
        self.is_training_self = other




# 定义一个栈式自编码器的类,继承自nn.Module
class StackedAutoEncoder(nn.Module):
    """
    Construct the whole network with layers_list
    > 栈式自编码器的架构一般是关于中间隐层对称的
    """


    # 定义初始化方法,接受一个自编码器层的列表作为参数
    def __init__(self, layers_list=None):
        super(StackedAutoEncoder, self).__init__() # 调用父类的初始化方法
        self.layers_list = layers_list # 保存自编码器层的列表
        self.initialize() # 调用初始化方法,将所有层的逐层预训练标志设为False
        # 将列表中的四个自编码器层分别命名为encoder_1, encoder_2, encoder_3, encoder_4
        self.encoder_1 = self.layers_list[0]
        self.encoder_2 = self.layers_list[1]
        self.encoder_3 = self.layers_list[2]
        self.encoder_4 = self.layers_list[3]


    # 定义一个初始化方法,将所有层的逐层预训练标志设为False
    def initialize(self):
        for layer in self.layers_list:
            # assert isinstance(layer, AutoEncoderLayer)
            layer.is_training_layer = False
            # for param in layer.parameters():
            #     param.requires_grad = True


    # 定义前向传播方法,接受输入x
    def forward(self, x):
        out = x # 将x赋值给out
        # 遍历自编码器层的列表,将out依次通过每一层
        # for layer in self.layers_list:
        #     out = layer(out)
        # 也可以直接使用命名的四个自编码器层
        out = self.encoder_1(out)
        out = self.encoder_2(out)
        out = self.encoder_3(out)
        out = self.encoder_4(out)
        return out # 返回最终的输出

run.py

# 导入系统库
import sys
# 将上一级目录添加到系统路径中,以便导入其他模块
sys.path.append('../')


# 从common.datas模块中导入get_mnist_loader函数,用于获取MNIST数据集的加载器
#from common.datas import get_mnist_loader
# 从models模块中导入AutoEncoderLayer和StackedAutoEncoder类,分别用于定义自编码器层和栈式自编码器模型
from models import AutoEncoderLayer, StackedAutoEncoder
# 导入torch库,用于构建和训练神经网络
import torch
# 从torch.nn模块中导入BCELoss类,用于计算二元交叉熵损失函数
from torch.nn import BCELoss
# 从torch模块中导入optim子模块,用于优化神经网络的参数
from torch import optim
# 导入torchvision库,用于处理图像数据
import torchvision
# 从torchvision.datasets模块中导入MNIST类,用于获取MNIST数据集
from torchvision.datasets import MNIST




# 定义一些超参数,包括逐层预训练的轮数,整体训练的轮数,批量大小,是否打乱数据等
num_tranin_layer_epochs = 20
num_tranin_whole_epochs = 50
batch_size = 100
shuffle = True




# 定义一个函数,用于获取MNIST数据集的加载器,接受批量大小和是否打乱数据的参数
def get_mnist_loader(batch_size=100, shuffle=True):
    """


    :return: train_loader, test_loader
    """
    # 创建一个训练数据集的对象,指定数据集的根目录,是否为训练集,是否进行图像转换(转换为张量),是否下载数据集
    train_dataset = MNIST(root='../data',
                          train=True,
                          transform=torchvision.transforms.ToTensor(),
                          download=True)
    # 创建一个测试数据集的对象,指定数据集的根目录,是否为训练集,是否进行图像转换(转换为张量),是否下载数据集
    test_dataset = MNIST(root='../data',
                         train=False,
                         transform=torchvision.transforms.ToTensor(),
                         download=True)


    # 创建一个训练数据加载器的对象,指定数据集,批量大小,是否打乱数据
    train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                               batch_size=batch_size,
                                               shuffle=shuffle)
    # 创建一个测试数据加载器的对象,指定数据集,批量大小,是否打乱数据
    test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                              batch_size=batch_size,
                                              shuffle=shuffle)
    # 返回训练数据加载器和测试数据加载器
    return train_loader, test_loader


# 定义一个函数,用于训练自编码器层,接受自编码器层的列表,要训练的层的索引,训练轮数,是否验证等参数
def train_layers(layers_list=None, layer=None, epoch=None, validate=True):
    # 如果GPU可用,将所有层转移到GPU上
    if torch.cuda.is_available():
        for model in layers_list:
            model.cuda()
    # 调用get_mnist_loader函数,获取训练数据和测试数据的加载器
    train_loader, test_loader = get_mnist_loader(batch_size=batch_size, shuffle=True)
    # 创建一个优化器,使用SGD算法,学习率为0.001,优化要训练的层的参数
    optimizer = optim.SGD(layers_list[layer].parameters(), lr=0.001)
    # 创建一个损失函数,使用二元交叉熵损失函数
    criterion = BCELoss()


    # 训练
    # 遍历训练轮数
    for epoch_index in range(epoch):
        # 初始化总损失为0
        sum_loss = 0.


        # 如果要训练的层不是第0层,将前面的层的梯度锁定,并将逐层预训练的标志设为False
        if layer != 0:
            for index in range(layer):
                layers_list[index].lock_grad()
                layers_list[index].is_training_layer = False 


        # 遍历训练数据加载器,得到每个批次的数据和标签(标签在这里不需要)
        for batch_index, (train_data, _) in enumerate(train_loader):
            # 如果GPU可用,将数据转移到GPU上
            if torch.cuda.is_available():
                train_data = train_data.cuda()
            # 将数据展平为一维向量
            out = train_data.view(train_data.size(0), -1)


            # 如果要训练的层不是第0层,将数据依次通过前面的层,得到该层的输入
            if layer != 0:
                for l in range(layer):
                    out = layers_list[l](out)


            # 训练第layer层,将输入通过该层,得到输出
            pred =  layers_list[layer](out)


            # 清空优化器的梯度缓存
            optimizer.zero_grad()
            # 计算输出和输入的二元交叉熵损失
            loss = criterion(pred, out)
            # 累加总损失
            sum_loss += loss
            # 反向传播,计算梯度
            loss.backward()
            # 更新参数
            optimizer.step()
            # 如果达到日志间隔,打印训练信息
            if (batch_index + 1) % 10 == 0:
                print("Train Layer: {}, Epoch: {}/{}, Iter: {}/{}, Loss: {:.4f}".format(
                    layer, (epoch_index + 1), epoch, (batch_index + 1), len(train_loader), loss
                ))


        # 如果需要验证,执行验证过程
        if validate:
            pass




# 定义一个函数,用于训练整个栈式自编码器模型,接受模型,训练轮数,是否验证等参数
def train_whole(model=None, epoch=50, validate=True):
    # 打印开始训练的信息
    print(">> start training whole model")
    # 如果GPU可用,将模型转移到GPU上
    if torch.cuda.is_available():
        model.cuda()


    # 将模型的所有参数的梯度解锁,即更新参数
    for param in model.parameters():
        param.require_grad = True


    # 调用get_mnist_loader函数,获取训练数据和测试数据的加载器
    train_loader, test_loader = get_mnist_loader(batch_size=batch_size, shuffle=shuffle)
    # 创建一个优化器,使用SGD算法,学习率为0.001,优化模型的所有参数
    optimizer = optim.SGD(model.parameters(), lr=0.001)
    # 创建一个损失函数,使用均方误差损失函数
    # criterion = BCELoss()
    criterion = torch.nn.MSELoss()


    # 从测试数据加载器中获取一批测试数据,并保存为图片
    test_data, _ = next(iter(test_loader))
    torchvision.utils.save_image(test_data, './test_images/real_test_images.png')


    # 训练
    # 遍历训练轮数
    for epoch_index in range(epoch):
        # 初始化总损失为0
        sum_loss = 0.
        # 遍历训练数据加载器,得到每个批次的数据和标签(标签在这里不需要)
        for batch_index, (train_data, _) in enumerate(train_loader):
            # 如果GPU可用,将数据转移到GPU上
            if torch.cuda.is_available():
                train_data = train_data.cuda()
            # 将数据展平为一维向量
            x = train_data.view(train_data.size(0), -1)


            # 将数据输入模型,得到输出
            out = model(x)


            # 清空优化器的梯度缓存
            optimizer.zero_grad()
            # 计算输出和输入的均方误差损失
            loss = criterion(out, x)
            # 累加总损失
            sum_loss += loss
            # 反向传播,计算梯度
            loss.backward()
            # 更新参数
            optimizer.step()


            # 如果达到日志间隔,打印训练信息
            if (batch_index + 1) % 10 == 0:
                print("Train Whole, Epoch: {}/{}, Iter: {}/{}, Loss: {:.4f}".format(
                    (epoch_index + 1), epoch, (batch_index + 1), len(train_loader), loss
                ))
            # 如果是最后一个批次,将输出重构为图片,并保存
            if batch_index == len(train_loader) - 1:
                torchvision.utils.save_image(out.view(100, 1, 28, 28), "./test_images/out_{}_{}.png".format(epoch_index, batch_index))


        # 每个轮数验证一次
        if validate:
            # 如果GPU可用,将测试数据转移到GPU上
            if torch.cuda.is_available():
                test_data = test_data.cuda()
            # 将测试数据展平为一维向量
            x = test_data.view(test_data.size(0), -1)
            # 将测试数据输入模型,得到输出
            out = model(x)
            # 计算输出和输入的均方误差损失
            loss = criterion(out, x)
            # 打印验证信息
            print("Test Epoch: {}/{}, Iter: {}/{}, test Loss: {}".format(
                epoch_index + 1, epoch, (epoch_index + 1), len(test_loader), loss
            ))




# 判断是否为主模块,如果是,则执行以下代码
if __name__ == '__main__':
    # 导入os库,用于操作系统相关的功能
    import os
    # 如果不存在test_images文件夹,就创建一个
    if not os.path.exists('test_images'):
        os.mkdir('test_images')
    # 如果不存在models文件夹,就创建一个
    if not os.path.exists('models'):
        os.mkdir('models')


    # 定义自编码器层数为5
    nun_layers = 5
    # 创建四个自编码器层的对象,分别为encoder_1, encoder_2, decoder_3, decoder_4,设置输入维度,输出维度和逐层预训练的标志
    encoder_1 = AutoEncoderLayer(784, 256, SelfTraining=True)
    encoder_2 = AutoEncoderLayer(256, 64, SelfTraining=True)
    decoder_3 = AutoEncoderLayer(64, 256, SelfTraining=True)
    decoder_4 = AutoEncoderLayer(256, 784, SelfTraining=True)
    # 将四个自编码器层的对象放入一个列表中,命名为layers_list
    layers_list = [encoder_1, encoder_2, decoder_3, decoder_4]


    # 按照顺序对每一层进行预训练
    # 遍历层数,从0到3
    for level in range(nun_layers - 1):
        # 调用train_layers函数,传入自编码器层的列表,要训练的层的索引,训练轮数,是否验证等参数,进行逐层预训练
        train_layers(layers_list=layers_list, layer=level, epoch=num_tranin_layer_epochs, validate=True)


    # 统一训练
    # 创建一个栈式自编码器的对象,传入自编码器层的列表,命名为SAE_model
    SAE_model = StackedAutoEncoder(layers_list=layers_list)
    # 调用train_whole函数,传入栈式自编码器的对象,训练轮数,是否验证等参数,进行整体训练
    train_whole(model=SAE_model, epoch=num_tranin_whole_epochs, validate=True)


    # 保存模型 refer: https://pytorch.org/docs/master/notes/serialization.html
    # 调用torch.save函数,传入栈式自编码器的对象和保存路径,将模型保存为sae_model.pt文件
    torch.save(SAE_model, './models/sae_model.pt')

输出结果:

eff1591b4af5a7fc74aab4a66dd58306.png

test_images文件夹

2434b27b3c7ef2127bf8b70c87bb1e27.png

real_test_images.png


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/276458.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

首次使用TypeScript,报错:无法重新声明块级范围变量(声明变量报错)

前几天在书写TypeScript代码时,出现了声明变量报错的情况,具体情况如下: let arr: number; arr 10; console.log(arr);报错如下: 解决方案: 在配置文件tsconfig.json中,配置如下代码: { &q…

js_常用事件演示

✨前言✨ 1.如果代码对您有帮助 欢迎点赞👍收藏⭐哟 后面如有问题可以私信评论哟🗒️ 2.博主后面将持续更新哟😘🎉文章目录 🍔一、在JavaScript中什么是事件?🍟二、为什么要使用事件&#x…

西软云XMS 反序列化RCE漏洞复现

0x01 产品简介 西软云XMS是基于云平台数据中心开发的支持多酒店、多语言、多平台的酒店管理系统。致力于以新一代云架构为国内四,五星级中高端酒店提供灵活、高度整合酒店业务,助力酒店智能转型升级。 0x02 漏洞概述 西软云XMS /fox-invoker/FoxLookupInvoker接口处存在反…

Erlang、RabbitMQ下载与安装教程(windows超详细)

目录 安装Erlang 1.首先安装RabbitMQ需要安装Erlang环境 2.点击下载好的.exe文件进行傻瓜式安装,一直next即可 3.配置Erlang环境变量 安装RabbitMQ 1.给出RabbitMQ官网下载址:Installing on Windows — RabbitMQ,找到 2.配置RabbitMQ环境变量&#xff0…

国际物流公司科普_集装箱种类区分和介绍_箱讯科技

集装箱运输的不断发展,为适应装载不同种类货物的需要,因而出现了不同种类的集装箱。今天和大家一起来总结一下。 按使用材料分类 根据箱子主体部件(侧壁、端壁、箱顶等)采用什么材料,就叫做什么材料制造的集装箱&…

机器学习的任务

根据学习任务的不同,机器学习算法大致可分为3类:监督学习、无监督学习和半监督学习。 监督学习(Supervised Learning) 在监督学习中,我们有一组带有标签(即已知输出)的训练样本作为输入。目标是…

Baumer工业相机堡盟工业相机如何通过BGAPI SDK实现Raw格式的图像保存(C++)

Baumer工业相机堡盟工业相机如何通过BGAPI SDK实现Raw格式的图像保存(C) Baumer工业相机Baumer工业相机通过SDK实现Raw格式的图像保存的技术背景通过SDK获取相机信息的代码分析Baumer工业相机回调函数里保存原始图像数据Baumer保存Raw图像格式重要核心代…

用Html和js和layui写一个简单猜拳小游戏

简单学习技术&#xff0c;写了一个小游戏&#xff0c;用html和js写一个简单的小游戏。玩家点击按钮出拳&#xff0c;玩家胜利结果显示绿色&#xff0c;玩家输了结果显示红色&#xff0c;平局结果显示蓝色。 页面效果&#xff1a; 代码&#xff1a; <!DOCTYPE html> <…

低代码平台快速开发CRM 可灵活自定义的CRM软件

白码低代码平台以其简化和加速应用程序开发的方法而闻名&#xff0c;无需大量编写代码&#xff0c;只需通过可视化界面和配置来构建应用程序。在快速开发CRM方面&#xff0c;白码低代码平台具有许多优势和应用。 白码低代码平台快速搭建CRM 快速开发是白码低代码平台的一大优势…

最新AI系统ChatGPT网站系统源码,Midjourney绘画,GPT语音对话+ChatFile文档对话总结+DALL-E3文生图+思维导图一站式解决方案

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作Ch…

MD5算法

一、引言 MD5&#xff08;Message-Digest Algorithm 5&#xff09;是一种广泛应用的密码散列算法&#xff0c;由Ronald L. Rivest于1991年提出。MD5算法主要用于对任意长度的消息进行加密&#xff0c;将消息压缩成固定长度的摘要&#xff08;通常为128位&#xff09;。在密码学…

算法设计与分析期末上机板子——课内题目题意与题解分析+课外知识点总结!

真正的模板&#xff01;&#xff01;&#xff01; 文章目录 课内堆实现C语言矩阵连乘E1D连分数计算C3A-钢管切割&#xff1a;动态规划C3C-流水线调度&#xff1a;动态规划C3E-矩阵连乘效率&#xff1a;区间动态规划C3F-导弹轰炸&#xff08;小偷问题&#xff09;&#xff1a;动…

flutter dio使用proxyman抓包进行网络调试

证书 wifi 手机和电脑连上同一个wifi&#xff0c;并且手机wifi使用代理&#xff0c;代理地址为电脑的ip和proxyman设置的监听端口 代码 import package:dio/dio.dart; import package:dio/io.dart; import dart:io;class ProxyUtil {static String proxyIP "";st…

MySQL GTID 主从错误

错误 搭建主从出现以下错误 Last_IO_Error: The replication receiver thread cannot start because the master has GTID_MODE OFF and this server has GTID_MODE ON. 原因 MySQL主从的 Master 和 Slave 必须 同时开启或者关闭 enforce-gtid-consistency和 gtid-mode 功能…

Centos如何修改ssh端口

想必很大一部分的同学用的是centos服务器&#xff0c;对于默认的22端口存在一定的安全风险&#xff0c;所以今天我们一起看下如何修改ssh端口 一、什么是SSH SSH&#xff08;Secure Shell&#xff09;是一种安全的远程登录协议&#xff0c;它允许您通过网络远程连接到Linux系统…

YOLOv5改进 | 2023主干篇 | 华为最新VanillaNet主干替换Backbone实现大幅度长点

一、本文介绍 本文给大家来的改进机制是华为最新VanillaNet网络&#xff0c;其是今年最新推出的主干网络&#xff0c;VanillaNet是一种注重极简主义和效率的神经网络架构。它的设计简单&#xff0c;层数较少&#xff0c;避免了像深度架构和自注意力这样的复杂操作(需要注意的是…

docker中部署mysql

原文链接&#xff1a; Docker 安装mysql8.0_docker安装mysql8.0-CSDN博客 1&#xff1a;拉取mysql镜像 docker pull mysql:8.0指定8.0版本的&#xff0c;因为我之前装的是5.6&#xff0c;不支持窗口函数&#xff0c;8版本之后的才支持&#xff0c;所以更换版本。 2&#xf…

C# WPF上位机开发(报表导出)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于在工厂上班的小伙伴来说&#xff0c;导出生产数据、生成报表&#xff0c;这是很习以为常的一个工作。之前的文章中&#xff0c;虽然我们也介绍…

格密码:隐藏超平面问题与uSVP问题 (Ajtai密码)

目录 一. 技术发展 二. 隐藏超平面问题&#xff08;hidden hyperplanes problem,HHP&#xff09; 三. 唯一最短向量问题&#xff0c;unique shortest vector problem&#xff0c;uSVP 四. Ajtai-Dwork密码系统&#xff08;改进版&#xff09; 4.1 公钥 4.2 私钥 4.3 加密…

Serverless架构:无服务器应用与AWS Lambda-读书笔记

Serverless架构&#xff1a;无服务器应用与AWS Lambda-读书笔记 好的架构可以成就软件&#xff0c;缺乏架构则会破坏软件。 一、Serverless 架构的来龙去脉 在典型的Web应用程序中&#xff0c;服务器接受前端的HTTP请求并处理请求。在保存到数据库之前&#xff0c;数据可能会…