Cet article est le premier jour de Furukawa Lab Advent_calendar. Cet article a été rédigé par un étudiant du Furukawa Lab dans le cadre de leur apprentissage. Le contenu peut être ambigu ou l'expression peut être légèrement différente.
Dans cet article, j'aimerais vous présenter ce qu'on appelle UKR (Unsupervised Kernel Regression) que j'ai en fait assemblé. Traduit en japonais, il s'agit d'une régression du noyau non supervisée, j'ai lu et implémenté cet article.
Si vous êtes intéressé par l'apprentissage multicorps, essayez-le.
Ce que UKR veut faire, ce sont les données d'observation $ \ mathbf {Y} = (\ mathbf {y} _1, \ mathbf {y} _2, ... \ mathbf {y} _N) \ in \ mathbb {R} ^ {D × Variable latente correspondant à N} $ et à chaque donnée d'observation
$ \ mathbf {X} = (\ mathbf {x} _1, \ mathbf {x} _2, ... \ mathbf {x} _N) \ in \ mathbb {R} ^ {M × N} Le mapping $ \ mathbf {f}: \ mathbb {R} ^ {M} → \ mathbb {R} ^ {D} $ doit être estimé.
Le mappage $ \ mathbf {f} $ est estimé par la formule suivante.
\mathbf{f}(\mathbf{x} ; \mathbf{X})=\sum_{i} \mathbf{y}_{i} \frac{K\left(\mathbf{x}-\mathbf{x}_{i}\right)}{\sum_{j} K\left(\mathbf{x}-\mathbf{x}_{j}\right)}\tag{1}
Peut-être que certaines personnes le connaissent. Cette formule est presque la même que l'estimation de Nadaraya-Watoson. Pour ceux qui ne sont pas familiers, $ K $ dans une expression s'appelle une fonction de noyau et est défini dans cet article avec l'expression suivante.
K\left(\mathbf{x}-\mathbf{x}^{\prime}\right)=\exp \left(\frac{-1}{2h}\left\|\mathbf{x}-\mathbf{x}^{\prime}\right\|^{2}\right)
Cette fonction du noyau est également appelée noyau gaussien. La seule différence avec Nadaraya-Watoson est que la largeur du noyau $ h $ est fixée à 1 $ dans l'article.
La variable latente est mappée à l'espace d'observation par la formule (1), et l'erreur des données d'observation est calculée par la formule suivante.
R(\mathbf{X})=\frac{1}{N} \sum_{i}\left\|\mathbf{y}_{i}-\mathbf{f}\left(\mathbf{x}_{i} ; \mathbf{X}\right)\right\|^{2}\tag{2}
Cette erreur est réduite par la méthode du gradient et la variable latente est mise à jour. Lors de sa mise en œuvre, vous n'avez pas d'autre choix que d'utiliser la différenciation automatique ou d'effectuer vous-même la différenciation et d'écrire la formule dans le programme. Dans cet article, je fais de mon mieux pour utiliser la formule que je me suis différenciée. Si vous écrivez toutes les extensions d'expressions, ce sera assez long et le fardeau pour moi sera grand, donc je ne posterai que le résultat final.
r_{ij}=\frac{K\left(\mathbf{x}_{i}-\mathbf{x}_{j}\right)}{\sum_{j^{\prime}} K\left(\mathbf{x}_{i}-\mathbf{x}_{j^{\prime}}\right)}
\mathbf{d}_{ij}=\mathbf{f}(\mathbf{x}_{j};\mathbf{X})-\mathbf{y}_{i}
\mathbf{\delta}_{ij}=\mathbf{x}_{i}-\mathbf{x}_{j}
En utilisant ces variables, la différenciation de l'équation (2) est exprimée comme suit.
\frac{1}{N} \frac{\partial}{\partial \mathbf{x}_{n}} \sum_{i}\left\|\mathbf{y}_{i}-\mathbf{f}(\mathbf{x}_{i};\mathbf{X})\right\|^{2}=\frac{2}{N}\sum_{i}\left[r_{n i} \mathbf{d}_{n n}^{\mathrm{T}} \mathbf{d}_{n i} \boldsymbol{\delta}_{n i}-r_{i n} \mathbf{d}_{i i}^{\mathrm{T}} \mathbf{d}_{i n} \boldsymbol{\delta}_{i n}\right]
Dans l'ensemble Estimer la cartographie par l'équation (1) Répétez la mise à jour de la variable latente par la méthode du gradient afin que l'erreur soit minimisée dans l'équation (2). Ce sera.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
from numpy.random import*
import matplotlib.animation as anm
fig = plt.figure(1,figsize=(14, 6))
ax1 = fig.add_subplot(121,projection='3d')
ax2 = fig.add_subplot(122)
class UKR:
def __init__(self,N,w,z,D,L):
self.N = N
self.zeta = z
self.x = w
self.f = []
self.hist =[]
self.sigma = 1.0
self.lamda = 0.1
self.test = []
self.lr = 1.0
self.D = D
self.L = L
self.gamma = 1.0 / (self.sigma * self.sigma)
def fit(self,T):
self.history = {"z":np.zeros((T, self.N, self.L)),
"Y":np.zeros((T, self.N ,self.D))}
for t in range(T):
self.delta = self.zeta[:,None,:] - self.zeta[None,:,:]
self.h_kn = np.exp(-1 / (2*self.sigma ** 2) * np.sum((self.zeta[None, :, :] - self.zeta[:, None, :]) ** 2,axis=2))
self.g_k = np.sum(self.h_kn,axis=1)
self.r_ij = self.h_kn/self.g_k[:,None]
self.f = np.zeros((self.N,self.D))
self.f = self.r_ij @ self.x
self.d_ij = self.f[:,None,:] - self.x[None,:,:]
self.A = self.gamma * self.r_ij * np.einsum('nd,nid->ni', self.f - self.x, self.d_ij)
self.bibun = np.sum((self.A + self.A.T)[:, :, None] * self.delta, axis=1)
self.zeta -= self.lr*(self.bibun + self.lamda*self.zeta)
self.history["Y"][t] = self.f
self.history["z"][t] = self.zeta
if __name__=="__main__":
N=20#Le nombre de données
T=50
w = np.zeros((N*N,3))
latent_space=np.random.normal
zeta = np.dstack(np.meshgrid(np.linspace(-1,1,N),np.linspace(-1,1,N),indexing='ij'))
zeta = np.reshape(zeta,(N*N,2))
for i in range (N*N):
mesh = zeta[i,0]**2-zeta[i,1]**2
w[i] = np.append(zeta[i],mesh)
np.random.seed(1)
i=0
ukr = UKR(N*N,w,zeta,3,2)
ukr.fit(T)
kekka = ukr.history["Y"]
kekka = np.reshape(kekka,(T,N,N,3))
k = ukr.history["z"]
k = np.array(k)
def update(i, zeta,w):
print(i)
ax1.cla()
ax2.cla()
ax1.scatter(w[:, 0], w[:, 1], w[:, 2], c=w[:, 0])
ax1.plot_wireframe(kekka[i,:,:,0],kekka[i,:,:,1],kekka[i,:,:,2])
ax2.scatter(k[i,:,0],k[i,:,1],c=w[:,0])
ax1.set_xlabel("X_axis")
ax1.set_ylabel("Y_axis")
ax1.set_zlabel("Z_axis")
ax2.set_title('latentspace')
ax2.set_xlabel("X_axis")
ax2.set_ylabel("Y_axis")
ani = anm.FuncAnimation(fig, update, fargs = (zeta,w), interval = 500, frames = T-1)
ani.save("test.gif", writer = 'imagemagick')
Il s'agit du résultat de la mise en œuvre lorsque les données de type selle sont données comme données d'observation. La figure de gauche montre l'espace d'observation, où chaque point montre les données d'observation et la variété qui relie les données mappées. La figure de droite montre l'espace latent.
C'est bien que les variétés couvrent progressivement les données d'observation, il est difficile de se lasser de les voir.
Variants of unsupervised kernel regression: General cost functions
Recommended Posts