下面我就以一些动漫头像为例,来说明怎样利用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\\facess\\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 j in range(len(imgs)):
img=imgs[j]
count+=1
batch.append(img)
if count==BATCH_SIZE or j==len(imgs)-1:#这里就算最后
#列表转成张量,再转换维度
batch_train=torch.Tensor(batch).permute(0,3,2,1)/255#batch,3,96,96
raw=batch_train.contiguous().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 j in range(100):
batch.append(imgs[j])
batch_train=torch.Tensor(batch).permute(0,3,2,1)/255
raw=batch_train.contiguous().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.contiguous().view(3,96,96).permute(2,1,0).detach().cpu().numpy()*255
cv2.imwrite("imgs/" + str(i)+str(j)+str(k) + ".jpg",cv2.cvtColor(img,cv2.COLOR_BGR2GRAY))
cv2.waitKey(0)
下面是给解码器三个数字,然后生成的图像
我感觉生成的图像样式基本都一样,只是颜色不一样而已。我就怀疑这些生成的图片只是数据集中的图片的平均,然后就了一个程序,然后输出所有图片的平均值,得到的结果和使用自编码网络生成的图片基本一致,可能原因是网络太深,中间层的神经元数量太少。所以如果要用自编码作为生成器的话,可能还需要很多其他的策略。但是对于mnist数据来说的话,如果解码器生成的图片是所有数据集的平均的话,那么很显然得到的就不是一个数字了,实际程序发现,对于随机的输入一个数字,生成的确实也是数字的样子,并不是所有图片的平均,但是可能是某个类别的平均。
自编码器相当于通过编码器数据集投影到低纬度的表示空间,一般来说会损失一些信息,然后通过解码器把低维的数据,映射到高维的数据。那么利用训练好的网络中的编码器就可以实现降维并可视化的功能,利用解码器就可以从低维空间中选取一些点然后生成最后的数据。数据量再多也是不可能占满低维空间的所有位置的,因为空间中的点的个数是无限的,所以这就给扩充数据集提供了可能性。
以上均为自己理解,难免会有偏差,欢迎评论、指正!