张春成
2022/11/03阅读:36主题:默认主题
自学习的生成模型小例
自学习的生成模型小例
自学习和生成式模型是机器学习领域的新兴方法。
这种方法的特点是不依赖样本标注,仅通过一些简单的规则就能够生成期望的结构。
本文试图训练这样一个极简模型,它通过自学习的方法,学会从平面生成类似球面的结构。
模型设计和损失函数
这是一个相当简单的模型,它的输入是 2 维向量,输出是 3 维向量,中间层分成两块,一块是全连接层,用于表达(或者说编码);另一块还是全连接层,用于解释(或者说解码)。你可以说它是 MLP 或者别的什么,这不重要。

模型结构
我训练它的方式是在二维平面上找一个区域,然后随机撒上一些点,成为一个随机点阵。
.png)
随机点阵
之后这些点输入到模型中,它的损失函数不需要对这些点进行标注,只是把它们随机分成两组,损失函数是这两组数据之间的余弦相似度。也就是说,我需要它们在映射后的空间中距离尽可能地远。
如此反复,在经过一段时间的训练后,它会自然地形成如下的结构。也就是说,它会自发地形成一个球面。
而这些网络参数则是这样一种映射关系,它将正方形的平面区域映射成了球形的结构。它们的位置具有对应关系,可以用于将一些二维图像映射到三维空间中的球形表面。

随机点阵映射图 1

随机点阵映射图 2
图形映射
为了较好地表示新模型的映射效果,我找到了这样一张图,它挺漂亮,也能够明显地区分区域。

映射原图
只要对它进行采样就可以得到一个填色的平面区域。
.png)
映射原图采样
接下来,用新模型将它们映射到球面上,可以得到下图。可以看到,它是一个缺了一角的圆球。

球面映射 1

球面映射 2
我最初的想法是从纸片生成一个圆球,但显然失败了,因为无论我怎么尝试(试了一天)都没有让这个形状封闭上。但转念一想,既然圆球和平面并不同胚,那么最初的目标可能是一个不容易企及的任务。就这样吧。
分析代码
本文分析代码如下,愿你有一块高性能的显卡
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchsummary import summary
import numpy as np
from collections import OrderedDict
import plotly.express as px
import pandas as pd
from tqdm.auto import tqdm
from PIL import Image
import requests
from io import BytesIO
print(torch.cuda.is_available())
## Generation network
class MyNetwork(torch.nn.Module):
def __init__(self):
super(MyNetwork, self).__init__()
self.fc1 = nn.Linear(2, 4)
self.fc2 = nn.Linear(4, 12)
self.act1 = nn.ReLU()
self.fc3 = nn.Linear(12, 6)
self.fc4 = nn.Linear(6, 3)
self.dropout = nn.Dropout(p=0.2)
def forward(self, ipt, allow_dropout=True):
v = ipt
v = self.fc1(v)
v = self.fc2(v)
v = self.act1(v)
if allow_dropout:
v = self.dropout(v)
v = self.fc3(v)
if allow_dropout:
v = self.dropout(v)
v = self.fc4(v)
v = F.normalize(v)
return v
def eval(self, ipt):
return self.forward(ipt, allow_dropout=False)
network = MyNetwork().cuda()
summary(network, (1, 2))
network
## Self learning
loss_func = nn.CosineSimilarity()
opt = torch.optim.Adam(network.parameters(), lr=0.05)
scheduler = torch.optim.lr_scheduler.StepLR(opt, step_size=1e3, gamma=0.8)
repeat = int(1e4)
n1 = int(5e2)
n2 = n1 * 2
_select = np.array(range(n2))
loss_trace = []
lowest_output = (1e10, None)
for step in tqdm(range(repeat)):
src = np.random.rand(n2, 2) - 0.5
loss = None
for _ in range(2):
output = network.forward(torch.Tensor(src).cuda())
np.random.shuffle(_select)
if loss is None:
loss = loss_func(output[_select[:n1]],
output[_select[n1:]]).sum() ** 2 # .abs()
else:
loss += loss_func(output[_select[:n1]],
output[_select[n1:]]).sum() ** 2 # .abs()
opt.zero_grad()
loss.backward()
opt.step()
scheduler.step()
# Clamp parameters
for p in network.parameters():
p.data.clamp_(-2, 2)
loss_trace.append(loss.item())
if loss_trace[-1] < lowest_output[0] and False:
print('update')
lowest_output = (loss_trace[-1], output)
if step % 100 == 0:
print('Loss: {}, {}'.format(step, loss_trace[-1]))
px.line(loss_trace, log_y=True).show()
## Latest result
src_color = [
'#{}{}00'.format(
hex(int(e[0] * 100 + 120)).replace('x', '')[-2:],
hex(int(e[1] * 100 + 120)).replace('x', '')[-2:]) for e in src
]
df = pd.DataFrame(src, columns=['x', 'y'])
fig = px.scatter(df, x='x', y='y')
fig.data[0]['marker']['color'] = src_color
fig.show()
df
# d = lowest_output[1].cpu().detach().numpy()
d = output.cpu().detach().numpy()
df = pd.DataFrame(d, columns=['x', 'y', 'z'])
df['size'] = 1
fig = px.scatter_3d(df, x='x', y='y', z='z', size='size', size_max=10)
fig.data[0]['marker']['color'] = src_color
fig.data[0]['marker']['line'] = {'width': 0}
fig.show()
df
## Render with image
url = 'https://images.unsplash.com/photo-1558470598-a5dda9640f68?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1171&q=80'
resp = requests.get(url)
img = Image.open(BytesIO(resp.content))
img = img.resize((100, 100))
img
mat = np.array(img)
sz = mat.shape
src1 = []
color1 = []
xgrid, ygrid = np.meshgrid(np.linspace(-0.5, 0.5, sz[0]), np.linspace(-0.5, 0.5, sz[1]))
for x in tqdm(range(sz[0]), 'Parse img'):
for y in range(sz[1]):
src1.append([xgrid[x, y], ygrid[x, y]])
color1.append('#' + ''.join([
hex(e).replace('x', '')[-2:] for e in mat[x, y]
]))
src1 = np.array(src1)
print(src1.shape)
color1[:8], src1[:8]
df = pd.DataFrame(src1, columns=['x', 'y'])
fig = px.scatter(df, x='x', y='y')
fig.data[0]['marker']['color'] = color1
fig['layout']['yaxis']['autorange'] = "reversed"
fig.show()
df
output1 = network.eval(torch.Tensor(src1).cuda())
d = output1.cpu().detach().numpy()
df = pd.DataFrame(d, columns=['x', 'y', 'z'])
df['size'] = 1
fig = px.scatter_3d(df, x='x', y='y', z='z', size='size', size_max=5)
fig.data[0]['marker']['color'] = color1
fig.data[0]['marker']['line'] = {'width': 0}
fig.show()
df
作者介绍