PyTorch读取自己的本地图片数据集训练自编码器

版权声明:转载注明出处 https://blog.csdn.net/york1996/article/details/82151106

下面我就以一些动漫头像为例,来说明怎样利用torch来进行训练和测试数据的预处理。下面是图片的格式:

动漫头像数据集

上述图片一共有51223张,每个图片的大小为3*96*96。 下载地址为:百度云链接

网络的基本结构是通过 卷积层*2,全连接层*n,解码层(全连接层*m)输入和输出的数据是一样的,最多是压缩到三个神经元。压缩到三个神经元的目的有两个,一个是可以对图片进行可视化,三个神经元代表三个坐标轴XYZ,另一个目的就是通过对三个神经元的随机赋值,再通过解码层生成一个张图片,相当于使用自编码器作为一个生成模型(效果可能很差)。

下面是构造自编码网络和训练这个网络的代码:

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt 
import cv2 
import os
#定义自编码器的网络结构
class AutoEncoder(nn.Module):
    def __init__(self):
        super(AutoEncoder, self).__init__()

        ###############################################################
        self.conv1=nn.Sequential(
            nn.Conv2d(
                in_channels=3,
                out_channels=16,
                kernel_size=3,
                stride=1,
                padding=1,
                    ),#->(16,96,96)
            nn.ReLU(),#->(16,96,96)
            nn.MaxPool2d(kernel_size=2),#->(16,48,48)
            )                     
        
        #->(16,48,48)
        ###############################################################
        self.conv2=nn.Sequential(
            nn.Conv2d(#->(16,48,48)
                in_channels=16,
                out_channels=32,
                kernel_size=3,
                stride=1,
                padding=1),#->(32,48,48)
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),#->(32,24,24)            
           
            )

        ###############################################################
        self.linear=nn.Sequential(
            nn.Linear( 32*24*24, 256 ), 
            nn.Tanh(),  # 激活函数
            nn.Linear( 256, 64 ), 
            nn.Tanh(),
            nn.Linear( 64, 12),
            nn.Tanh(),
            nn.Linear( 12 ,3),
            nn.Tanh()
            )
      
        #)
        self.decoder=nn.Sequential(
            nn.Linear(3,12),
            nn.Tanh(),
            nn.Linear( 12, 64 ),
            nn.Tanh(),
            nn.Linear( 64, 128 ),
            nn.Tanh(),
            nn.Linear( 128, 96*96*3),
            nn.Sigmoid()
        )
    def forward(self, x):
        x=self.conv1(x)
        x=self.conv2(x)
        x=x.view(x.size(0),-1)
        encoded=self.linear(x)
        decoded=self.decoder(encoded)
        return encoded,decoded
#训练并反向传播
def trainOneBatch(batch:torch.FloatTensor,raw:torch.FloatTensor):
    encoded,decoded=auto(batch)
    loss=loss_function(decoded,raw)

    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
#前向传播获得误差
def testOneBatch(batch:torch.FloatTensor,raw:torch.FloatTensor):
    encoded,decoded=auto(batch)
    loss=loss_function(decoded,raw)
    return loss
#超参数
LR=0.001
BATCH_SIZE=100
EPOCHES=30
#获取gpu是不是可用
cuda_available=torch.cuda.is_available()
#实例化网络
auto=AutoEncoder() 

if cuda_available :
    auto.cuda()

#定义优化器和损失函数
optimizer=torch.optim.Adam(auto.parameters(),lr=LR)
loss_function=nn.MSELoss()


#数据准备
DIRECTORY= "E:\\DataSets\\faces"#这里是自己的图片的位置
files=os.listdir(DIRECTORY)
imgs=[]#构造一个存放图片的列表数据结构
for file in files:
    file_path=DIRECTORY+"\\"+file
    img=cv2.imread(file_path)
    imgs.append(img)

print("train")

#遍历迭代期
for i in range(EPOCHES):
    print(i)
    #打乱数据
    np.random.shuffle(imgs)
    count=0#count是为了凑齐成为一个batch_size的大小
    batch=[]

    for i in range(len(imgs)):
        img=imgs[i]
        count+=1
        batch.append(img)

        if count==BATCH_SIZE or i==len(imgs)-1:#这里就算最后

            #列表转成张量,再转换维度
            batch_train=torch.Tensor(batch).permute(0,3,2,1)/255#batch,3,96,96
            raw=batch_train.view(batch_train.size(0),-1)#batch,3*96*96
            if cuda_available:
                raw=raw.cuda()#数据变换到gpu上
                batch_train=batch_train.cuda()
            trainOneBatch(batch_train,raw)#训练一个批次
            batch.clear()
            count=0
    batch.clear()
    #测试
    for i in range(100):
        batch.append(imgs[i])
        batch_train=torch.Tensor(batch).permute(0,3,2,1)/255
        raw=batch_train.view(batch_train.size(0),-1)
        if cuda_available:
            raw=raw.cuda()
            batch_train=batch_train.cuda()
    #调用函数获得损失
    loss=testOneBatch(batch_train,raw)
    batch.clear()
    print(loss)
    #把训练的中间结果输出到本地文件
    torch.save(auto,"auto.pkl")

下面是读取训练完成之后的网络,然后进行生成图像的代码:


import torch
import torch.nn as nn
import numpy as np

import cv2
class AutoEncoder(nn.Module):
    def __init__(self):
        super(AutoEncoder, self).__init__()
        #self.encoder=nn.Sequential(     #->(3,96,96)
        ###############################################################
        self.conv1=nn.Sequential(
            nn.Conv2d(
                in_channels=3,
                out_channels=16,
                kernel_size=3,
                stride=1,
                padding=1,
                    ),#->(16,96,96)
            nn.ReLU(),#->(16,96,96)
            nn.MaxPool2d(kernel_size=2),#->(16,48,48)
            )                     
        
        #->(16,48,48)
        ###############################################################
        self.conv2=nn.Sequential(
            nn.Conv2d(#->(16,48,48)
                in_channels=16,
                out_channels=32,
                kernel_size=3,
                stride=1,
                padding=1),#->(32,48,48)
            nn.ReLU(),
            nn.MaxPool2d(kernel_size=2),#->(32,24,24)            
           
            )

        ###############################################################
        self.linear=nn.Sequential(
            nn.Linear( 32*24*24, 256 ), 
            nn.Tanh(),  # 激活函数
            nn.Linear( 256, 64 ), 
            nn.Tanh(),
            nn.Linear( 64, 12),
            nn.Tanh(),
            nn.Linear( 12 ,3),
            nn.Tanh()
            )
      
        #)
        self.decoder=nn.Sequential(
            nn.Linear(3,12),
            nn.Tanh(),
            nn.Linear( 12, 64 ),
            nn.Tanh(),
            nn.Linear( 64, 128 ),
            nn.Tanh(),
            nn.Linear( 128, 96*96*3),
            nn.Sigmoid()
        )
    def forward(self, x):
        x=self.conv1(x)
        x=self.conv2(x)
        x=x.view(x.size(0),-1)
        encoded=self.linear(x)
        decoded=self.decoder(encoded)
        return encoded,decoded

auto:AutoEncoder=torch.load("auto.pkl")
print(auto)
auto=auto.cuda()
for i in range(6):
    for j in range(6):
        for k in range(6):
            m=i/2.5-1
            n=i/2.5-1
            p=k/2.5-1
            print(m,n,p)
            x=torch.FloatTensor([m,n,p])
            decoded=auto.decoder(x.cuda())
            img=decoded.view(3,96,96).permute(2,1,0).detach().cpu().numpy()*255
            cv2.imwrite("imgs/" + str(i)+str(j)+str(k) + ".jpg",img)

cv2.waitKey(0)

下面是给解码器三个数字,然后生成的图像

我感觉生成的图像样式基本都一样,只是颜色不一样而已。我就怀疑这些生成的图片只是数据集中的图片的平均,然后就了一个程序,然后输出所有图片的平均值,得到的结果和使用自编码网络生成的图片基本一致,可能原因是网络太深,中间层的神经元数量太少。所以如果要用自编码作为生成器的话,可能还需要很多其他的策略。但是对于mnist数据来说的话,如果解码器生成的图片是所有数据集的平均的话,那么很显然得到的就不是一个数字了,实际程序发现,对于随机的输入一个数字,生成的确实也是数字的样子,并不是所有图片的平均,但是可能是某个类别的平均。

自编码器相当于通过编码器数据集投影到低纬度的表示空间,一般来说会损失一些信息,然后通过解码器把低维的数据,映射到高维的数据。那么利用训练好的网络中的编码器就可以实现降维并可视化的功能,利用解码器就可以从低维空间中选取一些点然后生成最后的数据。数据量再多也是不可能占满低维空间的所有位置的,因为空间中的点的个数是无限的,所以这就给扩充数据集提供了可能性。

以上均为自己理解,难免会有偏差,欢迎评论、指正!

猜你喜欢

转载自blog.csdn.net/york1996/article/details/82151106