@ai-on-browser/data-analysis-models
Version:
Data analysis model package without any dependencies
177 lines (162 loc) • 5.51 kB
JavaScript
import Matrix from '../util/matrix.js'
import Tensor from '../util/tensor.js'
import NeuralNetwork from './neuralnetwork.js'
/**
* Diffusion model network
*/
export default class DiffusionModel {
// https://qiita.com/pocokhc/items/5a015ee5b527a357dd67
/**
* @param {number} timesteps Number of timestep
* @param {LayerObject[]} [layers] Layers
*/
constructor(timesteps, layers) {
this._timesteps = timesteps
this._ulayers = layers
this._peDims = 32
this._model = null
this._epoch = 0
const betaStart = 0.0001
const betaEnd = 0.02
const betaStep = (betaEnd - betaStart) / (this._timesteps - 1)
this._beta = [betaStart]
for (let t = 1; t < this._timesteps - 1; t++) {
this._beta[t] = betaStart + betaStep * t
}
this._beta.push(betaEnd)
this._alpha = [1 - this._beta[0]]
this._alphaCumprod = [this._alpha[0]]
for (let t = 1; t < this._beta.length; t++) {
this._alpha[t] = 1 - this._beta[t]
this._alphaCumprod[t] = this._alphaCumprod[t - 1] * this._alpha[t]
}
}
/**
* Epoch
* @type {number}
*/
get epoch() {
return this._epoch
}
_addNoise(x, t) {
const at = this._alphaCumprod[t]
const sqrtat = Math.sqrt(at)
const sqrt1at = Math.sqrt(1 - at)
const noize = Tensor.randn(x.sizes)
const xNoised = x.copy()
xNoised.broadcastOperate(noize, (a, b) => sqrtat * a + sqrt1at * b)
return [xNoised, noize]
}
_build() {
if (this._dataShape.length === 1) {
this._layers = [
{ type: 'input', name: 'x' },
{ type: 'input', name: 'position_encoding' },
{ type: 'full', out_size: this._peDims, l2_decay: 0.001, activation: 'gelu', name: 'pe' },
{ type: 'concat', input: ['x', 'pe'], axis: 1 },
]
if (this._ulayers) {
this._layers.push(...this._ulayers)
} else {
this._layers.push(
{ type: 'full', out_size: 32, l2_decay: 0.001, name: 'c1', activation: 'tanh' },
{ type: 'full', out_size: 16, l2_decay: 0.001, activation: 'tanh' },
{ type: 'full', out_size: 32, l2_decay: 0.001, name: 'u1', activation: 'tanh' },
{ type: 'concat', input: ['u1', 'c1'], axis: 1 },
{ type: 'full', out_size: 32, l2_decay: 0.001, activation: 'tanh' }
)
}
this._layers.push({ type: 'full', out_size: this._dataShape[0], l2_decay: 0.001 }, { type: 'output' })
} else {
const dim = this._dataShape.length
this._layers = [
{ type: 'input', name: 'x' },
{ type: 'input', name: 'position_encoding' },
{ type: 'full', out_size: this._peDims, l2_decay: 0.001, activation: 'gelu' },
{ type: 'reshape', size: [...Array(dim - 1).fill(1), this._peDims] },
{ type: 'up_sampling', size: this._dataShape.slice(0, dim - 1), name: 'pe' },
{ type: 'concat', input: ['x', 'pe'], axis: dim },
]
if (this._ulayers) {
this._layers.push(...this._ulayers)
} else {
this._layers.push(
{
type: 'conv',
kernel: 3,
channel: 16,
padding: 1,
l2_decay: 0.001,
name: 'c1',
activation: 'relu',
},
{ type: 'max_pool', kernel: 2 },
{ type: 'conv', kernel: 3, channel: 32, padding: 1, l2_decay: 0.001, activation: 'relu' },
{ type: 'up_sampling', size: 2, name: 'u1' },
{ type: 'concat', input: ['u1', 'c1'], axis: dim },
{ type: 'conv', kernel: 3, channel: 16, padding: 1, l2_decay: 0.001, activation: 'relu' }
)
}
this._layers.push(
{ type: 'conv', kernel: 1, channel: this._dataShape[dim - 1], l2_decay: 0.001 },
{ type: 'output' }
)
}
return NeuralNetwork.fromObject(this._layers, 'mse', 'adam')
}
_positionEncoding(t, embdims) {
const rates = Array.from({ length: embdims }, (_, i) => t / 10000 ** (2 * Math.floor(i / 2)) / embdims)
const pe = rates.map((v, i) => (i % 2 === 0 ? Math.sin(v) : Math.cos(v)))
return new Matrix(1, embdims, pe)
}
/**
* Fit model.
* @param {Array<Array<number>>} train_x Training data
* @param {number} iteration Iteration count
* @param {number} rate Learning rate
* @param {number} batch Batch size
* @returns {{labeledLoss: number, unlabeledLoss: number}} Loss value
*/
fit(train_x, iteration, rate, batch) {
const x = Tensor.fromArray(train_x)
this._dataShape = x.sizes.slice(1)
if (!this._model) {
this._model = this._build()
}
let loss = null
for (let i = 0; i < iteration; i++) {
const t = Math.floor(Math.random() * this._timesteps)
const pe = this._positionEncoding(t, this._peDims)
pe.repeat(x.sizes[0], 0)
const [noised_x, noise] = this._addNoise(x, t)
loss = this._model.fit({ x: noised_x, position_encoding: pe }, Tensor.fromArray(noise), 1, rate, batch)
}
this._epoch += iteration
return loss
}
/**
* Returns generated data from the model.
* @param {number} n Number of generated data
* @returns {Array<Array<number>>} Generated values
*/
generate(n) {
const ds = this._dataShape.concat()
const samples = Tensor.randn([n, ...ds])
for (let t = this._timesteps - 1; t >= 0; t--) {
const pe = this._positionEncoding(t, this._peDims)
pe.repeat(n, 0)
const pred = this._model.calc({ x: samples, position_encoding: pe })
samples.broadcastOperate(
pred,
(a, b) =>
(1 / Math.sqrt(this._alpha[t])) * (a - (b * this._beta[t]) / Math.sqrt(1 - this._alphaCumprod[t]))
)
if (t > 0) {
const s2 = ((1 - this._alphaCumprod[t - 1]) / (1 - this._alphaCumprod[t])) * this._beta[t]
const noise = Tensor.randn(samples.sizes, 0, s2)
samples.broadcastOperate(noise, (a, b) => a + b)
}
}
return samples.toArray()
}
}