提交 197f443b 编写于 作者: O o2null

Merge branch 'feature/index' into 'develop'

Feature/index

See merge request o2oa/o2oa!1275
package com.x.base.core.neural;
import java.util.List;
import com.x.base.core.neural.mlp.Data;
public interface DataSet<E extends Data> {
public DataSet<E> sub(int fromIndex, int toIndex);
public DataSet<E> filter(int label);
public DataSet<E> choice(int... arr);
public DataSet<E> choice(List<Integer> list);
public DataSet<E> randomChoice(int size);
}
package com.x.base.core.neural.cnn;
import java.util.concurrent.CompletableFuture;
public class ConvolutionTools {
public static final double DELTA = 1e-10;
private ConvolutionTools() {
// nothing
}
public static double[][] sum(double[][] x, double[][] y) {
if (null == x) {
return y;
}
if (null == y) {
return x;
}
double[][] o = new double[x.length][x[0].length];
for (int i = 0; i < x.length; i++) {
for (int j = 0; j < x[0].length; j++) {
o[i][j] = x[i][j] + y[i][j];
}
}
return o;
}
public static double[][] add(double[][] x, double b) {
double[][] o = new double[x.length][x[0].length];
for (int i = 0; i < x.length; i++) {
for (int j = 0; j < x[0].length; j++) {
o[i][j] = x[i][j] + b;
}
}
return o;
}
public static double[][] conv(double[][] x, double[][] y, int stride) {
int h = ((x.length - y.length) / stride) + 1;
int w = ((x[0].length - y[0].length) / stride) + 1;
double[][] o = new double[h][w];
@SuppressWarnings("unchecked")
CompletableFuture<Void>[] futures = new CompletableFuture[h];
for (int i = 0; i < h; i++) {
futures[i] = convFuture(x, y, stride, w, o, i);
}
try {
CompletableFuture.allOf(futures).get();
} catch (Exception e) {
e.printStackTrace();
}
return o;
}
private static CompletableFuture<Void> convFuture(double[][] x, double[][] y, int stride, int w, double[][] o,
int i) {
return CompletableFuture.runAsync(() -> {
for (int j = 0; j < w; j++) {
int xh = i * stride;
int xw = j * stride;
double s = 0d;
for (int yh = 0; yh < y.length; yh++) {
for (int yw = 0; yw < y[0].length; yw++) {
s += x[xh + yh][xw + yw] * y[yh][yw];
}
}
o[i][j] = s;
}
});
}
public static double[][] flip(double[][] x) {
int h = x.length;
int w = x[0].length;
double[][] o = new double[h][w];
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
o[i][j] = x[h - i - 1][w - j - 1];
}
}
return o;
}
public static double[][] dot(double[][] x, double[][] y) {
double[][] o = new double[x.length][y[0].length];
@SuppressWarnings("unchecked")
CompletableFuture<Void>[] futures = new CompletableFuture[o.length];
for (int i = 0; i < o.length; i++) {
futures[i] = dotFuture(x, y, o, i);
}
try {
CompletableFuture.allOf(futures).get();
} catch (Exception e) {
e.printStackTrace();
}
return o;
}
private static CompletableFuture<Void> dotFuture(double[][] x, double[][] y, double[][] o, int i) {
return CompletableFuture.runAsync(() -> {
for (int j = 0; j < o[0].length; j++) {
double v = 0d;
for (int k = 0; k < x[0].length; k++) {
v += x[i][k] * y[k][j];
}
o[i][j] = v;
}
});
}
public static double[][] transpose(double[][] x) {
double[][] y = new double[x[0].length][x.length];
for (int i = 0; i < x.length; i++) {
for (int j = 0; j < x[0].length; j++) {
y[j][i] = x[i][j];
}
}
return y;
}
public static double[][] subtract(double[][] x, double[][] y) {
double[][] o = new double[x.length][x[0].length];
for (int i = 0; i < x.length; i++) {
for (int j = 0; j < x[0].length; j++) {
o[i][j] = x[i][j] - y[i][j];
}
}
return o;
}
public static double sum(double[][] x) {
double s = 0f;
for (int i = 0; i < x.length; i++) {
for (int j = 0; j < x[0].length; j++) {
s += x[i][j];
}
}
return s;
}
public static double max(final double... array) {
double max = array[0];
for (int j = 1; j < array.length; j++) {
if (array[j] > max) {
max = array[j];
}
}
return max;
}
public static int argmax(double[] arr) {
double max = arr[0];
int idx = 0;
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
idx = i;
}
}
return idx;
}
}
package com.x.base.core.neural.cnn;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import com.x.base.core.neural.cnn.layer.Layer;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public class Network {
private String name;
private String description;
private List<Layer> layers = new ArrayList<>();
public String description() {
return Objects.toString(this.description, "");
}
public Network description(String description) {
this.description = description;
return this;
}
public String name() {
return Objects.toString(this.name, "");
}
public Network name(String name) {
this.name = name;
return this;
}
public List<Layer> layers() {
return this.layers;
}
public void layers(List<Layer> list) {
this.layers = list;
}
public ConvolutionMatrix predict(ConvolutionMatrix x) {
for (Layer layer : layers) {
x = layer.forward(x);
}
return x;
}
}
package com.x.base.core.neural.cnn.dump;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.google.gson.Gson;
import com.x.base.core.neural.cnn.Network;
import com.x.base.core.neural.cnn.layer.Affine;
import com.x.base.core.neural.cnn.layer.Convolution;
import com.x.base.core.neural.cnn.layer.Layer;
import com.x.base.core.neural.cnn.layer.Pooling;
import com.x.base.core.neural.cnn.layer.activation.Relu;
import com.x.base.core.neural.cnn.loss.Loss;
import com.x.base.core.neural.cnn.loss.SoftmaxWithCrossEntropyError;
import com.x.base.core.neural.cnn.train.TrainNetwork;
public class Dump {
private String name;
private String description;
private Map<Integer, Convolution> convolutions = new TreeMap<>();
private Map<Integer, Pooling> poolings = new TreeMap<>();
private Map<Integer, Relu> relus = new TreeMap<>();
private Map<Integer, Affine> affines = new TreeMap<>();
private SoftmaxWithCrossEntropyError softmaxWithCrossEntropyError = null;
public Dump(TrainNetwork trainNetwork) {
this.name = trainNetwork.name();
this.description = trainNetwork.description();
for (int i = 0; i < trainNetwork.layers().size(); i++) {
Layer layer = trainNetwork.layers().get(i);
if (layer instanceof Convolution) {
convolutions.put(Integer.valueOf(i), (Convolution) layer);
} else if (layer instanceof Pooling) {
poolings.put(Integer.valueOf(i), (Pooling) layer);
} else if (layer instanceof Relu) {
relus.put(Integer.valueOf(i), (Relu) layer);
} else if (layer instanceof Affine) {
affines.put(Integer.valueOf(i), (Affine) layer);
}
}
Loss loss = trainNetwork.loss();
if (loss instanceof SoftmaxWithCrossEntropyError) {
this.softmaxWithCrossEntropyError = (SoftmaxWithCrossEntropyError) loss;
}
}
private List<Layer> layers() {
List<Layer> list = new ArrayList<>();
for (int i = 0; i < sizeOfLayers(); i++) {
final int j = i;
convolutions.entrySet().stream().filter(o -> j == o.getKey()).findFirst()
.ifPresent(o -> list.add(o.getValue()));
poolings.entrySet().stream().filter(o -> j == o.getKey()).findFirst()
.ifPresent(o -> list.add(o.getValue()));
relus.entrySet().stream().filter(o -> j == o.getKey()).findFirst().ifPresent(o -> list.add(o.getValue()));
affines.entrySet().stream().filter(o -> j == o.getKey()).findFirst().ifPresent(o -> list.add(o.getValue()));
}
return list;
}
private int sizeOfLayers() {
return this.convolutions.size() + this.poolings.size() + this.relus.size() + this.affines.size();
}
public static Dump formJson(String json) {
Gson gson = new Gson();
return gson.fromJson(json, Dump.class);
}
public String toJson() {
Gson gson = new Gson();
return gson.toJson(this);
}
public Network toNetwork() {
Network network = new Network();
network.name(this.name).description(this.description);
network.layers(this.layers());
return network;
}
public TrainNetwork toTrainNetwork() {
TrainNetwork network = new TrainNetwork(name) {
@Override
public void init() {
// nothing
}
};
network.name(name).description(description).layers(this.layers());
if (null != this.softmaxWithCrossEntropyError) {
network.loss(this.softmaxWithCrossEntropyError);
}
return network;
}
}
\ No newline at end of file
package com.x.base.core.neural.cnn.layer;
import com.x.base.core.neural.cnn.ConvolutionTools;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
import com.x.base.core.neural.cnn.optimizer.Optimizer;
public class Affine implements Layer {
private String name;
private ConvolutionMatrix w;
private ConvolutionMatrix dw;
private ConvolutionMatrix b;
private ConvolutionMatrix db;
private ConvolutionMatrix x;
private Optimizer optimizer;
public String name() {
return this.name;
}
public Affine w(ConvolutionMatrix w) {
this.w = w;
this.dw = new ConvolutionMatrix(w.number(), w.channel(), w.height(), w.width());
return this;
}
public Affine b(ConvolutionMatrix b) {
this.b = b;
this.db = new ConvolutionMatrix(b.number(), b.channel(), b.height(), b.width());
return this;
}
public Affine optimizer(Optimizer optimizer) {
this.optimizer = optimizer;
return this;
}
public ConvolutionMatrix b() {
return this.b;
}
public ConvolutionMatrix w() {
return this.w;
}
public ConvolutionMatrix dw() {
return this.dw;
}
public ConvolutionMatrix x() {
return this.x;
}
public Optimizer optimizer() {
return this.optimizer;
}
@Override
public ConvolutionMatrix forward(ConvolutionMatrix x) {
this.x = x;
double[][] arr = this.reshape(x);
double[][] o = ConvolutionTools.dot(arr, this.reshape(this.w()));
ConvolutionMatrix m = new ConvolutionMatrix(o.length, 1, 1, o[0].length);
for (int i = 0; i < o.length; i++) {
m.set(i, 0, 0, o[i]);
}
return m;
}
@Override
public ConvolutionMatrix backward(ConvolutionMatrix dy) {
this.dwBackward(dy);
this.dbBackward(dy);
double[][] arr = ConvolutionTools.dot(reshape(dy), ConvolutionTools.transpose(reshape(this.w())));
return reshape(arr);
}
private double[][] reshape(ConvolutionMatrix x) {
double[][] arr = new double[x.number()][x.channel() * x.height() * x.width()];
int sizeOfChannel = x.height() * x.width();
x.visit((n, c, h, w, v) -> arr[n][(c * sizeOfChannel) + (h * x.width()) + w] = v);
return arr;
}
private ConvolutionMatrix reshape(double[][] arr) {
ConvolutionMatrix o = new ConvolutionMatrix(x.number(), x.channel(), x.height(), x.width());
int sizeOfChannel = x.height() * x.width();
o.visit((n, c, h, w, v) -> o.set(n, c, h, w, arr[n][(c * sizeOfChannel) + (h * x.width()) + w]));
return o;
}
private void dwBackward(ConvolutionMatrix dy) {
double[][] o = new double[x.number()][x.channel() * x.height() * x.width()];
int sizeOfChannel = x.height() * x.width();
for (int n = 0; n < x.number(); n++) {
for (int c = 0; c < x.channel(); c++) {
for (int h = 0; h < x.height(); h++) {
for (int width = 0; width < x.width(); width++) {
o[n][c * sizeOfChannel + h * x.width() + width] = x.get(n, c, h, width);
}
}
}
}
double[][] values = ConvolutionTools.dot(ConvolutionTools.transpose(o), this.reshape(dy));
for (int n = 0; n < values.length; n++) {
this.dw.set(n, 0, 0, values[n]);
}
}
private void dbBackward(ConvolutionMatrix dy) {
for (int i = 0; i < dy.width(); i++) {
double v = 0d;
for (int j = 0; j < dy.height(); j++) {
v += dy.get(0, 0, j, i);
}
db.set(i, 0, 0, 0, v);
}
}
@Override
public void update() {
optimizer.update(w, dw);
optimizer.update(b, db);
}
}
package com.x.base.core.neural.cnn.layer;
import java.util.concurrent.CompletableFuture;
import com.x.base.core.neural.cnn.ConvolutionTools;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
import com.x.base.core.neural.cnn.optimizer.Optimizer;
public class Convolution implements Layer {
// https://zhuanlan.zhihu.com/p/40951745
// https://www.cnblogs.com/pinard/p/6494810.html
private int stride = 1;
private int pad = 0;
private int padValue = 0;
private ConvolutionMatrix x;
private ConvolutionMatrix w;
private ConvolutionMatrix b;
private ConvolutionMatrix dw;
private ConvolutionMatrix db;
private Optimizer optimizer;
public ConvolutionMatrix x() {
return this.x;
}
public int stride() {
return this.stride;
}
public Convolution stride(int stride) {
this.stride = stride;
return this;
}
public int pad() {
return this.pad;
}
public Convolution pad(int pad) {
this.pad = pad;
return this;
}
public int padValue() {
return this.padValue;
}
public Convolution padValue(int padValue) {
this.padValue = padValue;
return this;
}
public Convolution w(ConvolutionMatrix w) {
this.w = w;
this.dw = new ConvolutionMatrix(w.number(), w.channel(), w.height(), w.width());
return this;
}
public ConvolutionMatrix w() {
return this.w;
}
public Convolution b(ConvolutionMatrix b) {
this.b = b;
this.db = new ConvolutionMatrix(b.number(), b.channel(), b.height(), b.width());
return this;
}
public ConvolutionMatrix b() {
return this.b;
}
public Convolution db(ConvolutionMatrix db) {
this.db = db;
return this;
}
public ConvolutionMatrix db() {
return this.db;
}
public Convolution dw(ConvolutionMatrix dw) {
this.dw = dw;
return this;
}
public ConvolutionMatrix dw() {
return this.dw;
}
public Convolution optimizer(Optimizer optimizer) {
this.optimizer = optimizer;
return this;
}
public Optimizer optimizer() {
return this.optimizer;
}
public ConvolutionMatrix forward(ConvolutionMatrix x) {
if (this.pad > 0) {
this.x = x.pad(this.pad, this.padValue);
} else {
this.x = x.copy();
}
int rh = ((this.x().height() - this.w().height()) / this.stride()) + 1;
int rw = ((this.x().width() - this.w().width()) / this.stride()) + 1;
ConvolutionMatrix y = new ConvolutionMatrix(x.number(), this.w().number(), rh, rw);
@SuppressWarnings("unchecked")
CompletableFuture<Void>[] futrues = new CompletableFuture[x.number()];
for (int xn = 0; xn < x.number(); xn++) {
futrues[xn] = forwardFuture(xn, y);
}
try {
CompletableFuture.allOf(futrues).get();
} catch (Exception e) {
e.printStackTrace();
}
return y;
}
private CompletableFuture<Void> forwardFuture(int xn, ConvolutionMatrix y) {
return CompletableFuture.runAsync(() -> {
for (int wn = 0; wn < this.w().number(); wn++) {
y.set(xn, wn, ConvolutionTools.add(forwardChannelConv(xn, wn), this.b().get(wn, 0, 0, 0)));
}
});
}
private double[][] forwardChannelConv(int xn, int wn) {
double[][] o = null;
for (int c = 0; c < this.w().channel(); c++) {
o = ConvolutionTools.sum(o, ConvolutionTools.conv(this.x().get(xn, c), this.w().get(wn, c), this.stride()));
}
return o;
}
@Override
public ConvolutionMatrix backward(ConvolutionMatrix y) {
this.dbBackward(y);
this.dwBackward(y);
return this.dxBackward(y);
}
private void dbBackward(ConvolutionMatrix y) {
for (int c = 0; c < y.channel(); c++) {
double s = 0d;
for (int n = 0; n < y.number(); n++) {
s += ConvolutionTools.sum(y.get(n, c));
}
this.db().set(c, 0, 0, 0, s);
}
}
private void dwBackward(ConvolutionMatrix y) {
for (int wn = 0; wn < this.w().number(); wn++) {
for (int wc = 0; wc < this.w().channel(); wc++) {
double[][] o = null;
for (int yn = 0; yn < y.number(); yn++) {
o = ConvolutionTools.sum(o,
ConvolutionTools.conv(this.x().get(yn, wc), y.get(yn, wn), this.stride()));
}
this.dw().set(wn, wc, o);
}
}
}
private ConvolutionMatrix dxBackward(ConvolutionMatrix y) {
y = y.pad(this.w().width() - 1, 0);
ConvolutionMatrix dx = new ConvolutionMatrix(y.number(), y.channel(), this.x().height(), this.x().width());
for (int yn = 0; yn < y.number(); yn++) {
for (int wc = 0; wc < this.w().channel(); wc++) {
double[][] o = null;
for (int wn = 0; wn < this.w().number(); wn++) {
o = ConvolutionTools.sum(o, ConvolutionTools.conv(y.get(yn, wn),
ConvolutionTools.flip(this.w().get(wn, wc)), this.stride()));
}
dx.set(yn, wc, o);
}
}
if (this.pad() > 0) {
dx = dx.reduce(this.pad());
}
return dx;
}
@Override
public void update() {
optimizer.update(w, dw);
optimizer.update(b, db);
}
}
package com.x.base.core.neural.cnn.layer;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public interface Layer {
ConvolutionMatrix forward(ConvolutionMatrix x);
ConvolutionMatrix backward(ConvolutionMatrix y);
void update();
}
package com.x.base.core.neural.cnn.layer;
import java.util.concurrent.CompletableFuture;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public class Pooling implements Layer {
private int height;
private int width;
private int stride;
private int pad;
private ConvolutionMatrix x;
public int height() {
return this.height;
}
public int width() {
return this.width;
}
public int stride() {
return this.stride;
}
public int pad() {
return this.pad;
}
public ConvolutionMatrix x() {
return this.x;
}
public Pooling(int height, int width, int stride, int pad) {
this.height = height;
this.width = width;
this.stride = stride;
this.pad = pad;
}
@Override
public ConvolutionMatrix forward(ConvolutionMatrix x) {
if (this.pad() > 0) {
this.x = x.pad(1, 0);
} else {
this.x = x;
}
int oh = (this.x().height() - this.height()) / this.stride() + 1;
int ow = (this.x().width() - this.width()) / this.stride() + 1;
ConvolutionMatrix o = new ConvolutionMatrix(this.x().number(), this.x().channel(), oh, ow);
@SuppressWarnings("unchecked")
CompletableFuture<Void>[] futures = new CompletableFuture[x.number()];
for (int n = 0; n < this.x().number(); n++) {
futures[n] = forwardFuture(this.x(), oh, ow, o, n);
}
try {
CompletableFuture.allOf(futures).get();
} catch (Exception e) {
e.printStackTrace();
}
return o;
}
private CompletableFuture<Void> forwardFuture(ConvolutionMatrix x, int oh, int ow, ConvolutionMatrix o, int n) {
return CompletableFuture.runAsync(() -> {
for (int c = 0; c < x.channel(); c++) {
o.set(n, c, forward(x.get(n, c), oh, ow));
}
});
}
public double[][] forward(double[][] x, int oh, int ow) {
double[][] o = new double[oh][ow];
for (int i = 0; i < oh; i++) {
for (int j = 0; j < ow; j++) {
int xh = i * this.stride();
int xw = j * this.stride();
double max = Double.MIN_VALUE;
for (int h = 0; h < this.height(); h++) {
for (int w = 0; w < this.width(); w++) {
if (x[xh + h][xw + w] > max) {
max = x[xh + h][xw + w];
}
}
}
o[i][j] = max;
}
}
return o;
}
@Override
public ConvolutionMatrix backward(ConvolutionMatrix y) {
ConvolutionMatrix o = new ConvolutionMatrix(this.x().number(), this.x().channel(), this.x().height(),
this.x().width());
@SuppressWarnings("unchecked")
CompletableFuture<Void>[] futures = new CompletableFuture[y.number()];
for (int n = 0; n < y.number(); n++) {
futures[n] = backwardFuture(y, n, o);
}
try {
CompletableFuture.allOf(futures).get();
} catch (Exception e) {
e.printStackTrace();
}
if (this.pad() > 0) {
o = o.reduce(this.pad());
}
return o;
}
private CompletableFuture<Void> backwardFuture(ConvolutionMatrix y, int n, ConvolutionMatrix o) {
return CompletableFuture.runAsync(() -> {
for (int c = 0; c < y.channel(); c++) {
o.set(n, c, this.backward(n, c, y.get(n, c)));
}
});
}
private double[][] backward(int n, int c, double[][] y) {
double[][] o = new double[this.x().height()][this.x().width()];
for (int i = 0; i < y.length; i++) {
for (int j = 0; j < y[0].length; j++) {
int xh = i * this.stride();
int xw = j * this.stride();
double max = Double.MIN_VALUE;
int maxh = 0;
int maxw = 0;
for (int h = 0; h < this.height(); h++) {
for (int w = 0; w < this.width(); w++) {
if (this.x().get(n, c, xh + h, xw + w) > max) {
max = this.x().get(n, c, xh + h, xw + w);
maxh = xh + h;
maxw = xw + w;
}
}
}
o[maxh][maxw] = o[maxh][maxw] + y[i][j];
}
}
return o;
}
@Override
public void update() {
// nothing
}
}
\ No newline at end of file
package com.x.base.core.neural.cnn.layer.activation;
import com.x.base.core.neural.cnn.layer.Layer;
public interface Activation extends Layer {
}
package com.x.base.core.neural.cnn.layer.activation;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public class Relu implements Activation {
private ConvolutionMatrix mask;
@Override
public ConvolutionMatrix forward(ConvolutionMatrix x) {
this.mask = new ConvolutionMatrix(x.number(), x.channel(), x.height(), x.width());
x.visit((n, c, h, w, v) -> {
if (v > 0d) {
mask.set(n, c, h, w, v);
} else {
mask.set(n, c, h, w, 0);
}
});
return mask.copy();
}
@Override
public ConvolutionMatrix backward(ConvolutionMatrix y) {
ConvolutionMatrix dx = new ConvolutionMatrix(y.number(), y.channel(), y.height(), y.width());
y.visit((n, c, h, w, v) -> {
if (mask.get(n, c, h, w) > 0) {
dx.set(n, c, h, w, v);
} else {
dx.set(n, c, h, w, 0d);
}
});
return dx;
}
// @Override
public void update() {
// nothing
}
}
package com.x.base.core.neural.cnn.loss;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public interface Loss {
ConvolutionMatrix backward();
double forward(ConvolutionMatrix x, ConvolutionMatrix t);
}
package com.x.base.core.neural.cnn.loss;
import com.x.base.core.neural.cnn.ConvolutionTools;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public class SoftmaxWithCrossEntropyError implements Loss {
private double[][] y;
private double[][] rt;
private ConvolutionMatrix x;
public double forward(ConvolutionMatrix x, ConvolutionMatrix t) {
this.x = x;
// System.out.println("x:" + x);
double[][] rx = reshape(x);
this.y = softmax(rx);
this.rt = reshape(t);
return crossEntropyError(this.y, rt);
}
private double[][] reshape(ConvolutionMatrix m) {
double[][] o = new double[m.number() * m.channel() * m.height()][m.width()];
for (int n = 0; n < m.number(); n++) {
o[n] = m.get(n, 0, 0);
}
return o;
}
private ConvolutionMatrix reshape(double[][] arr) {
ConvolutionMatrix m = new ConvolutionMatrix(arr.length, 1, 1, arr[0].length);
for (int n = 0; n < arr.length; n++) {
m.set(n, 0, 0, arr[n]);
}
return m;
}
public ConvolutionMatrix backward() {
double batch = this.rt.length;
double[][] o = ConvolutionTools.subtract(this.y, this.rt);
ConvolutionMatrix m = this.reshape(o);
m.visit((n, c, h, w, v) -> m.set(n, c, h, w, v / batch));
return m;
}
private double crossEntropyError(double[][] x, double[][] t) {
double sum = 0d;
for (int i = 0; i < x.length; i++) {
double[] xrow = x[i];
double[] trow = t[i];
int hot = ConvolutionTools.argmax(trow);
sum += Math.log(xrow[hot] + ConvolutionTools.DELTA);
}
return -(sum / (double) x.length);
}
private double[][] softmax(double[][] x) {
double[][] o = new double[x.length][x[0].length];
for (int i = 0; i < x.length; i++) {
double[] rowData = x[i];
double max = ConvolutionTools.max(rowData);
for (int j = 0; j < rowData.length; j++) {
rowData[j] = Math.exp(rowData[j] - max);
}
double sum = 0d;
for (double d : rowData) {
sum += d;
}
if (0d == sum) {
sum = ConvolutionTools.DELTA;
}
for (int j = 0; j < rowData.length; j++) {
rowData[j] = rowData[j] / sum;
}
o[i] = rowData;
}
return o;
}
}
\ No newline at end of file
package com.x.base.core.neural.cnn.matrix;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
public class ConvolutionMatrix {
private double[][][][] data;
private int number;
private int channel;
private int height;
private int width;
public ConvolutionMatrix(int number, int channel, int height, int width) {
this.number = number;
this.channel = channel;
this.height = height;
this.width = width;
this.data = new double[number][channel][height][width];
}
public int number() {
return this.number;
}
public double[][][] get(int n) {
return this.data[n];
}
public double[][] get(int n, int c) {
return this.data[n][c];
}
public double[] get(int n, int c, int h) {
return this.data[n][c][h];
}
public double get(int n, int c, int h, int w) {
return this.data[n][c][h][w];
}
public ConvolutionMatrix set(double v) {
for (int n = 0; n < this.number(); n++) {
for (int c = 0; c < this.channel(); c++) {
for (int h = 0; h < this.height(); h++) {
for (int w = 0; w < this.width(); w++) {
set(n, c, h, w, v);
}
}
}
}
return this;
}
public ConvolutionMatrix set(int n, double[][][] v) {
this.data[n] = v;
return this;
}
public ConvolutionMatrix set(int n, int c, double[][] v) {
this.data[n][c] = v;
return this;
}
public ConvolutionMatrix set(int n, int c, int h, double[] v) {
this.data[n][c][h] = v;
return this;
}
public ConvolutionMatrix set(int n, int c, int h, int w, double v) {
this.data[n][c][h][w] = v;
return this;
}
public ConvolutionMatrix number(int n, double[][][] value) {
data[n] = value;
return this;
}
public int channel() {
return this.channel;
}
public double[][] channel(int n, int c) {
return data[n][c];
}
public ConvolutionMatrix number(int n, int c, double[][] value) {
data[n][c] = value;
return this;
}
public int height() {
return this.height;
}
public double[] height(int n, int c, int h) {
return data[n][c][h];
}
public ConvolutionMatrix height(int n, int c, int h, double[] value) {
data[n][c][h] = value;
return this;
}
public int width() {
return this.width;
}
public double width(int n, int c, int h, int w) {
return data[n][c][h][w];
}
public ConvolutionMatrix width(int n, int c, int h, int w, double value) {
data[n][c][h][w] = value;
return this;
}
public ConvolutionMatrix pad(int size, double value) {
ConvolutionMatrix y = new ConvolutionMatrix(this.number(), this.channel(), this.height() + (size * 2),
this.width() + (size * 2));
@SuppressWarnings("unchecked")
CompletableFuture<Void>[] futrues = new CompletableFuture[y.number];
for (int n = 0; n < y.number(); n++) {
futrues[n] = padFuture(n, size, value, y);
}
try {
CompletableFuture.allOf(futrues).get();
} catch (Exception e) {
e.printStackTrace();
}
return y;
}
private CompletableFuture<Void> padFuture(int n, int size, double value, ConvolutionMatrix y) {
return CompletableFuture.runAsync(() -> {
for (int c = 0; c < y.channel(); c++) {
for (int h = 0; h < y.height(); h++) {
for (int w = 0; w < y.width(); w++) {
pad(n, size, value, y, c, h, w);
}
}
}
});
}
private void pad(int n, int size, double value, ConvolutionMatrix y, int c, int h, int w) {
if ((h < size) || (w < size) || ((h - size) >= this.height()) || ((w - size) >= this.width())) {
y.data[n][c][h][w] = value;
} else {
y.data[n][c][h][w] = this.data[n][c][h - size][w - size];
}
}
public ConvolutionMatrix reduce(int size) {
ConvolutionMatrix y = new ConvolutionMatrix(this.number(), this.channel(), this.height() - (size * 2),
this.width() - (size * 2));
System.out.println(this.dimensionToString());
System.out.println(y.dimensionToString());
@SuppressWarnings("unchecked")
CompletableFuture<Void>[] futrues = new CompletableFuture[y.number()];
for (int n = 0; n < y.number(); n++) {
futrues[n] = reduceFuture(n, size, y);
}
try {
CompletableFuture.allOf(futrues).get();
} catch (Exception e) {
e.printStackTrace();
}
return y;
}
private CompletableFuture<Void> reduceFuture(int n, int size, ConvolutionMatrix y) {
return CompletableFuture.runAsync(() -> {
for (int c = 0; c < y.channel(); c++) {
for (int h = 0; h < y.height(); h++) {
for (int w = 0; w < y.width(); w++) {
reduce(n, size, y, c, h, w);
}
}
}
});
}
private void reduce(int n, int size, ConvolutionMatrix y, int c, int h, int w) {
System.out.println(this.data[n][c][h + size][w + size]);
y.set(n, c, h, w, this.data[n][c][h + size][w + size]);
}
public double[][] strip() {
double[][] value = new double[this.number()][this.channel() * this.height() * this.width()];
for (int n = 0; n < this.number(); n++) {
double[] s = new double[this.channel() * this.height() * this.width()];
int i = 0;
for (int c = 0; c < this.channel(); c++) {
for (int h = 0; h < this.height(); h++) {
for (int w = 0; w < this.width(); w++) {
s[i++] = data[n][c][h][w];
}
}
}
value[n] = s;
}
return value;
}
public double[] stripOfNumber(int number, int fromHeight, int fromWidth, int toHeight, int toWidth) {
double[] value = new double[this.channel() * (toHeight - fromHeight + 1) * (toWidth - fromWidth + 1)];
int i = 0;
for (int c = 0; c < this.channel(); c++) {
for (int h = fromHeight; h <= toHeight; h++) {
for (int w = fromWidth; w <= toWidth; w++) {
value[i++] = data[number][c][h][w];
}
}
}
return value;
}
public double[] stripOfNumber(int number) {
double[] value = new double[this.channel() * this.height() * this.width()];
int i = 0;
for (int c = 0; c < this.channel(); c++) {
for (int h = 0; h < this.height(); h++) {
for (int w = 0; w < this.width(); w++) {
value[i++] = data[number][c][h][w];
}
}
}
return value;
}
public ConvolutionMatrix copy() {
ConvolutionMatrix o = new ConvolutionMatrix(this.number(), this.channel(), this.height(), this.width());
for (int n = 0; n < this.number(); n++) {
for (int c = 0; c < this.channel(); c++) {
for (int h = 0; h < this.height(); h++) {
for (int w = 0; w < this.width(); w++) {
o.set(n, c, h, w, data[n][c][h][w]);
}
}
}
}
return o;
}
public void visit(Visitor visitor) {
for (int n = 0; n < this.number(); n++) {
for (int c = 0; c < this.channel(); c++) {
for (int h = 0; h < this.height(); h++) {
for (int w = 0; w < this.width(); w++) {
visitor.visit(n, c, h, w, this.get(n, c, h, w));
}
}
}
}
}
@FunctionalInterface
public interface Visitor {
void visit(int n, int c, int h, int w, double value);
}
@Override
public String toString() {
return Arrays.deepToString(this.data);
}
public String dimensionToString() {
return this.getClass() + ", n=" + this.number() + ", c=" + this.channel() + ", h=" + this.height() + ", w="
+ this.height() + ".";
}
}
\ No newline at end of file
package com.x.base.core.neural.cnn.mnist;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import org.apache.commons.io.IOUtils;
import com.x.base.core.neural.cnn.ConvolutionTools;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public class MNIST {
public static final String MNIST_TRAIN_IMAGES_FILE = "train-images.idx3-ubyte";
public static final String MNIST_TRAIN_LABELS_FILE = "train-labels.idx1-ubyte";
public static final String MNIST_TEST_IMAGES_FILE = "t10k-images.idx3-ubyte";
public static final String MNIST_TEST_LABELS_FILE = "t10k-labels.idx1-ubyte";
public static DataSet trainDataSet(boolean normalize) throws Exception {
return dataSet(MNIST_TRAIN_IMAGES_FILE, MNIST_TRAIN_LABELS_FILE, 60000, normalize);
}
public static DataSet testDataSet(boolean normalize) throws Exception {
return dataSet(MNIST_TEST_IMAGES_FILE, MNIST_TEST_LABELS_FILE, 10000, normalize);
}
private static DataSet dataSet(String imageFile, String labelFile, int count, boolean normalize) throws Exception {
DataSet set = new DataSet();
byte[] imageBytes = read(imageFile);
byte[] labelBytes = read(labelFile);
for (int i = 0; i < count; i++) {
double[][][] o = new double[1][28][28];
int n = 0;
for (int j = i * 28 * 28 + 16; j < (i + 1) * 28 * 28 + 16; j++) {
o[0][n % 28][n / 28] = Byte.toUnsignedInt(imageBytes[j]);
n++;
}
if (normalize) {
for (int a = 0; a < o.length; a++) {
for (int b = 0; b < o[0].length; b++) {
for (int c = 0; c < o[0][0].length; c++) {
o[a][b][c] = o[a][b][c] / 255d;
}
}
}
}
Data data = new Data();
data.input = o;
double[] label = new double[10];
Arrays.fill(label, 0d);
label[Byte.toUnsignedInt(labelBytes[8 + i])] = 1d;
data.label = label;
set.add(data);
}
return set;
}
public static class DataSet extends ArrayList<Data> {
private static final long serialVersionUID = 1L;
private Random random = new SecureRandom();
public DataSet sub(int fromIndex, int toIndex) {
DataSet set = new DataSet();
for (int i = fromIndex; i < toIndex; i++) {
set.add(this.get(i));
}
return set;
}
public DataSet filter(int label) {
DataSet set = new DataSet();
for (Data data : this) {
if (ConvolutionTools.argmax(data.label) == label) {
set.add(data);
}
}
return set;
}
public DataSet choice(int... arr) {
DataSet set = new DataSet();
for (int i : arr) {
set.add(this.get(i));
}
return set;
}
public DataSet choice(List<Integer> list) {
DataSet set = new DataSet();
for (int i : list) {
set.add(this.get(i));
}
return set;
}
public DataSet randomChoice(int size) {
DataSet set = new DataSet();
random.ints(size, 0, this.size()).forEach(o -> set.add(this.get(o)));
return set;
}
public ConvolutionMatrix input() {
ConvolutionMatrix x = new ConvolutionMatrix(this.size(), 1, 28, 28);
for (int i = 0; i < x.number(); i++) {
x.set(i, this.get(i).input);
}
return x;
}
public ConvolutionMatrix label() {
ConvolutionMatrix x = new ConvolutionMatrix(this.size(), 1, 1, 10);
for (int i = 0; i < x.number(); i++) {
x.set(i, 0, 0, this.get(i).label);
}
return x;
}
}
public static class Data {
protected double[][][] input;
protected double[] label;
public double[][][] input() {
return input;
}
public double[] label() {
return label;
}
}
private static byte[] read(String fileName) throws Exception {
Path p = Paths.get(ClassLoader.getSystemResource(fileName).toURI());
byte[] bs = null;
try (InputStream in = Files.newInputStream(p)) {
bs = IOUtils.toByteArray(in);
}
return bs;
}
}
package com.x.base.core.neural.cnn.mnist;
public class MNISTImageTools {
private MNISTImageTools() {
// nothing
}
// public static void png(String imageFilePrefix, ConvolutionMatrix matrix) throws Exception {
// Path dir = Paths.get("image", imageFilePrefix);
// Files.createDirectories(dir);
// for (int n = 0; n < matrix.number(); n++) {
// WritableImage image = new WritableImage(matrix.height(), matrix.width());
// PixelWriter writer = image.getPixelWriter();
// for (int h = 0; h < matrix.height(); h++) {
// for (int w = 0; w < matrix.width(); w++) {
// int c = (int) matrix.get(n, 0, h, w);
// writer.setColor(h, w, Color.rgb(c, c, c));
// }
// }
// Path path = dir.resolve(String.format("%05d", n) + ".png");
// ImageIO.write(SwingFXUtils.fromFXImage(image, null), "png", path.toFile());
// }
// }
}
package com.x.base.core.neural.cnn.optimizer;
import com.x.base.core.neural.cnn.ConvolutionTools;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public class Adam implements Optimizer {
private double learningRate = 0.1d;
private double beta1 = 0.9d;
private double beta2 = 0.999d;
private int iter = 0;
private ConvolutionMatrix m;
private ConvolutionMatrix v;
public Adam() {
}
public Adam(double learningRate, double beta1, double beta2) {
this.learningRate = learningRate;
this.beta1 = beta1;
this.beta2 = beta2;
}
public void update(ConvolutionMatrix x, ConvolutionMatrix dx) {
if (m == null) {
m = new ConvolutionMatrix(x.number(), x.channel(), x.height(), x.width());
v = new ConvolutionMatrix(x.number(), x.channel(), x.height(), x.width());
}
iter++;
double temp = (learningRate * Math.sqrt(1.0 - Math.pow(beta2, iter)) / (1d - Math.pow(beta1, iter)));
x.visit((n, c, h, w, value) -> {
double d = dx.get(n, c, h, w);
double mv = m.get(n, c, h, w);
double vv = v.get(n, c, h, w);
mv = mv + (1d - beta1) * (d - mv);
vv = vv + ((1d - beta2) * (Math.pow(d, 2) - vv));
m.set(n, c, h, w, mv);
v.set(n, c, h, w, vv);
x.set(n, c, h, w, (value - ((temp * mv) / (Math.sqrt(vv) + ConvolutionTools.DELTA))));
});
}
public static class Param {
private double learningRate = 0.001d;
private double beta1 = 0.9d;
private double beta2 = 0.999d;
public double getLearningRate() {
return learningRate;
}
public void setLearningRate(double learningRate) {
this.learningRate = learningRate;
}
public double getBeta1() {
return beta1;
}
public void setBeta1(double beta1) {
this.beta1 = beta1;
}
public double getBeta2() {
return beta2;
}
public void setBeta2(double beta2) {
this.beta2 = beta2;
}
}
}
\ No newline at end of file
package com.x.base.core.neural.cnn.optimizer;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public interface Optimizer {
public void update(ConvolutionMatrix x, ConvolutionMatrix dx);
}
package com.x.base.core.neural.cnn.optimizer;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public class StochasticGradientDescent implements Optimizer {
private double learningRate = 0.01d;
public StochasticGradientDescent() {
}
public StochasticGradientDescent(double learningRate) {
this.learningRate = learningRate;
}
@Override
public void update(ConvolutionMatrix x, ConvolutionMatrix dx) {
x.visit((n, c, h, w, v) -> x.set(n, c, h, w, v - (learningRate * dx.get(n, c, h, w))));
}
}
package com.x.base.core.neural.cnn.train;
public interface EpochEndedListener {
}
package com.x.base.core.neural.cnn.train;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import com.x.base.core.neural.cnn.ConvolutionTools;
import com.x.base.core.neural.cnn.dump.Dump;
import com.x.base.core.neural.cnn.layer.Layer;
import com.x.base.core.neural.cnn.loss.Loss;
import com.x.base.core.neural.cnn.loss.SoftmaxWithCrossEntropyError;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
public abstract class TrainNetwork {
private String name;
private String description;
private List<Layer> layers = new ArrayList<>();
private Loss loss = new SoftmaxWithCrossEntropyError();
public String name() {
return Objects.toString(this.name, "");
}
public TrainNetwork name(String name) {
this.name = name;
return this;
}
public String description() {
return Objects.toString(this.description, "");
}
public TrainNetwork description(String description) {
this.description = description;
return this;
}
public List<Layer> layers() {
return this.layers;
}
public void layers(List<Layer> list) {
this.layers = list;
}
public Loss loss() {
return this.loss;
}
public TrainNetwork loss(Loss loss) {
this.loss = loss;
return this;
}
public TrainNetwork(String name) {
this.name = name;
this.init();
}
public abstract void init();
public TrainNetwork initParameter() {
return this;
}
public ConvolutionMatrix predict(ConvolutionMatrix x) {
for (Layer layer : layers()) {
x = layer.forward(x);
}
return x;
}
public TrainNetwork gradient(ConvolutionMatrix dy) {
for (int i = layers.size() - 1; i >= 0; i--) {
dy = layers.get(i).backward(dy);
}
return this;
}
public double accuracy(ConvolutionMatrix x, ConvolutionMatrix t) {
ConvolutionMatrix y = this.predict(x);
int match = 0;
for (int n = 0; n < y.number(); n++) {
for (int c = 0; c < y.channel(); c++) {
for (int h = 0; h < y.height(); h++) {
if (ConvolutionTools.argmax(y.get(n, c, h)) == ConvolutionTools.argmax(t.get(n, c, h))) {
match++;
}
}
}
}
return (match / (double) (y.number() * y.channel() * y.height()));
}
public TrainNetwork update() {
for (int i = layers().size() - 1; i >= 0; i--) {
layers().get(i).update();
}
return this;
}
public static class DumpThread extends Thread {
private TrainNetwork trainNetwork;
public DumpThread(TrainNetwork trainNetwork) {
this.trainNetwork = trainNetwork;
}
@Override
public void run() {
try {
Dump dump = new Dump(trainNetwork);
Path dir = Paths.get("dump");
Files.createDirectories(dir);
Path path = dir.resolve(trainNetwork.name() + ".json");
Files.writeString(path, dump.toJson(), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.x.base.core.neural.cnn.train;
import java.security.SecureRandom;
import java.util.Date;
import java.util.Random;
import com.x.base.core.neural.cnn.layer.Affine;
import com.x.base.core.neural.cnn.layer.Convolution;
import com.x.base.core.neural.cnn.layer.Pooling;
import com.x.base.core.neural.cnn.layer.activation.Relu;
import com.x.base.core.neural.cnn.loss.Loss;
import com.x.base.core.neural.cnn.loss.SoftmaxWithCrossEntropyError;
import com.x.base.core.neural.cnn.matrix.ConvolutionMatrix;
import com.x.base.core.neural.cnn.mnist.MNIST;
import com.x.base.core.neural.cnn.mnist.MNIST.DataSet;
import com.x.base.core.neural.cnn.optimizer.StochasticGradientDescent;
public class Training {
private static Random random = new SecureRandom();
private static TrainNetwork network = null;
public static void main(String... args) throws Exception {
network = new TrainNetwork("MNIST") {
@Override
public void init() {
this.layers().add(concreteConv01());
this.layers().add(concreteRelu02());
this.layers().add(concretePooling03());
this.layers().add(concreteAffine04());
this.layers().add(concreteRelu05());
this.layers().add(concreteAffine06());
this.loss(concreteLoss());
}
};
DataSet trainSet = MNIST.trainDataSet(true);
DataSet testSet = MNIST.testDataSet(true);
for (int i = 0; i < 600 * 10000; i++) {
DataSet set = trainSet.randomChoice(100);
ConvolutionMatrix y = network.predict(set.input());
network.loss().forward(y, set.label());
ConvolutionMatrix dy = network.loss().backward();
network.gradient(dy).update();
if (i % 100 == 0) {
DumpThread thread = new DumpThread(network, testSet);
thread.start();
}
}
}
private static Convolution concreteConv01() {
Convolution convolution = new Convolution();
ConvolutionMatrix w = new ConvolutionMatrix(30, 1, 5, 5);
w.visit((n, c, height, width, v) -> w.set(n, c, height, width, random.nextDouble() * 0.01));
ConvolutionMatrix b = new ConvolutionMatrix(30, 1, 1, 1);
convolution.w(w);
convolution.b(b);
convolution.stride(1);
convolution.pad(0);
convolution.optimizer(new StochasticGradientDescent());
return convolution;
}
private static Relu concreteRelu02() {
return new Relu();
}
private static Pooling concretePooling03() {
return new Pooling(2, 2, 2, 0);
}
private static Affine concreteAffine04() {
Affine affine = new Affine();
ConvolutionMatrix w = new ConvolutionMatrix(12 * 12 * 30, 1, 1, 100);
w.visit((n, c, height, width, v) -> w.set(n, c, height, width, random.nextDouble() * 0.01));
ConvolutionMatrix b = new ConvolutionMatrix(100, 1, 1, 1);
affine.w(w);
affine.b(b);
affine.optimizer(new StochasticGradientDescent());
return affine;
}
private static Relu concreteRelu05() {
return new Relu();
}
private static Affine concreteAffine06() {
Affine affine = new Affine();
ConvolutionMatrix w = new ConvolutionMatrix(100, 1, 1, 10);
w.visit((n, c, height, width, v) -> w.set(n, c, height, width, random.nextDouble() * 0.01));
ConvolutionMatrix b = new ConvolutionMatrix(10, 1, 1, 1);
affine.w(w);
affine.b(b);
affine.optimizer(new StochasticGradientDescent());
return affine;
}
private static Loss concreteLoss() {
return new SoftmaxWithCrossEntropyError();
}
public static class DumpThread extends Thread {
private TrainNetwork trainNetwork;
private DataSet testSet;
public DumpThread(TrainNetwork trainNetwork, DataSet testSet) {
this.trainNetwork = trainNetwork;
this.testSet = testSet;
}
@Override
public void run() {
try {
DataSet test = testSet.randomChoice(100);
double loss = trainNetwork.loss().forward(trainNetwork.predict(test.input()), test.label());
double accuracy = trainNetwork.accuracy(test.input(), test.label());
System.out.println(
trainNetwork.name() + "(" + (new Date()) + ")-> loss:" + loss + ", accuracy:" + accuracy + ".");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.x.base.core.neural.mlp;
public interface Data {
public float[] input();
public float[] label();
public int inputSize();
public int labelSize();
}
package com.x.base.core.neural.mlp;
import java.util.ArrayList;
import java.util.List;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class DataSet<E extends Data> extends ArrayList<E> implements com.x.base.core.neural.DataSet<E> {
private static final long serialVersionUID = 1L;
public DataSet<E> sub(int fromIndex, int toIndex) {
DataSet<E> set = new DataSet<>();
for (int i = fromIndex; i < toIndex; i++) {
set.add(this.get(i));
}
return set;
}
public DataSet<E> filter(int label) {
DataSet<E> set = new DataSet<>();
for (E d : this) {
if (MultilayerPerceptronTools.argmax(d.label()) == label) {
set.add(d);
}
}
return set;
}
public DataSet<E> choice(int... arr) {
DataSet<E> set = new DataSet<>();
for (int i : arr) {
set.add(this.get(i));
}
return set;
}
public DataSet<E> choice(List<Integer> list) {
DataSet<E> set = new DataSet<>();
for (int i : list) {
set.add(this.get(i));
}
return set;
}
public DataSet<E> randomChoice(int size) {
int[] ints = MultilayerPerceptronTools.uniqueRandomInts(size, 0, this.size());
DataSet<E> set = new DataSet<>();
for (int i : ints) {
set.add(this.get(i));
}
return set;
}
public MultilayerPerceptronMatrix input() {
MultilayerPerceptronMatrix x = new MultilayerPerceptronMatrix(this.size(), this.get(0).inputSize());
for (int i = 0; i < x.row(); i++) {
x.row(i, this.get(i).input());
}
return x;
}
public MultilayerPerceptronMatrix label() {
MultilayerPerceptronMatrix x = new MultilayerPerceptronMatrix(this.size(), this.get(0).labelSize());
for (int i = 0; i < x.row(); i++) {
x.row(i, this.get(i).label());
}
return x;
}
}
\ No newline at end of file
package com.x.base.core.neural.mlp;
import java.security.SecureRandom;
import java.util.Random;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.math.NumberUtils;
public class MultilayerPerceptronTools {
private static final Random random = new SecureRandom();
public static final float DELTA = 1e-10f;
private MultilayerPerceptronTools() {
// nothing
}
public static int argmax(float[] arr) {
float max = arr[0];
int idx = 0;
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
idx = i;
}
}
return idx;
}
public static float[] normalize0to1(final float[] sample) {
// (bla - min(bla)) / ( max(bla) - min(bla) )
float max = NumberUtils.max(sample);
float min = NumberUtils.min(sample);
float total = max - min;
float[] result = new float[sample.length];
if (total <= 0) {
for (int i = 0; i < sample.length; i++) {
result[i] = 1f;
}
} else {
for (int i = 0; i < sample.length; i++) {
result[i] = (sample[i] - min) / total;
}
}
return result;
}
public static int[] uniqueRandomInts(int count, int randomNumberOrigin, int randomNumberBound) {
int s = randomNumberBound - randomNumberOrigin;
int[] arr = new int[s];
for (int i = 0; i < s; i++) {
arr[i] = randomNumberOrigin + i;
}
ArrayUtils.shuffle(arr, random);
return ArrayUtils.subarray(arr, 0, count);
}
public static float sigmoid(float v) {
return 1f / (1f + (float) Math.exp(-v));
}
}
package com.x.base.core.neural.mlp;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import com.x.base.core.neural.mlp.layer.Layer;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class Network {
private String name;
private String description;
private int input;
private int output;
private List<Layer> layers = new ArrayList<>();
public String name() {
return Objects.toString(this.name, "");
}
public void name(String name) {
this.name = name;
}
public String description() {
return Objects.toString(this.description, "");
}
public void description(String description) {
this.description = description;
}
public int input() {
return this.input;
}
public void input(int input) {
this.input = input;
}
public int output() {
return this.output;
}
public void output(int output) {
this.output = output;
}
public List<Layer> layers() {
return this.layers;
}
public void layers(List<Layer> list) {
this.layers = list;
}
public MultilayerPerceptronMatrix predict(MultilayerPerceptronMatrix x) {
for (Layer layer : layers) {
x = layer.forward(x);
}
return x;
}
}
\ No newline at end of file
package com.x.base.core.neural.mlp.dump;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.x.base.core.neural.mlp.Network;
import com.x.base.core.neural.mlp.layer.Affine;
import com.x.base.core.neural.mlp.layer.BatchNormalization;
import com.x.base.core.neural.mlp.layer.Layer;
import com.x.base.core.neural.mlp.layer.activation.Relu;
import com.x.base.core.neural.mlp.layer.activation.Sigmoid;
import com.x.base.core.neural.mlp.layer.activation.Tanh;
import com.x.base.core.neural.mlp.loss.Loss;
import com.x.base.core.neural.mlp.loss.MeanSquareError;
import com.x.base.core.neural.mlp.loss.SigmoidWithMeanSquareError;
import com.x.base.core.neural.mlp.loss.SoftmaxWithCrossEntropyError;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
import com.x.base.core.neural.mlp.train.TrainNetwork;
public class Dump {
private String name;
private String description;
private int input;
private int output;
private int[] hiddens;
private Map<Integer, Affine> affines = new TreeMap<>();
private Map<Integer, BatchNormalization> batchNormalizations = new TreeMap<>();
private Map<Integer, Relu> relus = new TreeMap<>();
private Map<Integer, Sigmoid> sigmoids = new TreeMap<>();
private Map<Integer, Tanh> tanhs = new TreeMap<>();
private MeanSquareError meanSquareError = null;
private SigmoidWithMeanSquareError sigmoidWithMeanSquareError = null;
private SoftmaxWithCrossEntropyError softmaxWithCrossEntropyError = null;
private static Gson gson = new GsonBuilder().setPrettyPrinting()
.registerTypeAdapter(MultilayerPerceptronMatrix.class, new MultilayerPerceptronMatrixAdapter()).create();
// private static Gson gson = new GsonBuilder().setPrettyPrinting().create();
public Dump(TrainNetwork trainNetwork) {
this.name = trainNetwork.name();
this.description = trainNetwork.description();
this.input = trainNetwork.input();
this.output = trainNetwork.output();
this.hiddens = trainNetwork.hiddens();
for (int i = 0; i < trainNetwork.layers().size(); i++) {
Layer layer = trainNetwork.layers().get(i);
if (layer instanceof Affine) {
affines.put(Integer.valueOf(i), (Affine) layer);
} else if (layer instanceof BatchNormalization) {
batchNormalizations.put(Integer.valueOf(i), (BatchNormalization) layer);
} else if (layer instanceof Relu) {
relus.put(Integer.valueOf(i), (Relu) layer);
} else if (layer instanceof Sigmoid) {
sigmoids.put(Integer.valueOf(i), (Sigmoid) layer);
} else if (layer instanceof Tanh) {
tanhs.put(Integer.valueOf(i), (Tanh) layer);
}
}
Loss loss = trainNetwork.loss();
if (null != loss) {
if (loss instanceof MeanSquareError) {
this.meanSquareError = (MeanSquareError) loss;
} else if (loss instanceof SigmoidWithMeanSquareError) {
this.sigmoidWithMeanSquareError = (SigmoidWithMeanSquareError) loss;
} else if (loss instanceof SoftmaxWithCrossEntropyError) {
this.softmaxWithCrossEntropyError = (SoftmaxWithCrossEntropyError) loss;
}
}
}
private List<Layer> layers() {
List<Layer> list = new ArrayList<>();
for (int i = 0; i < sizeOfLayers(); i++) {
final int j = i;
affines.entrySet().stream().filter(o -> j == o.getKey()).findFirst().ifPresent(o -> list.add(o.getValue()));
batchNormalizations.entrySet().stream().filter(o -> j == o.getKey()).findFirst()
.ifPresent(o -> list.add(o.getValue()));
relus.entrySet().stream().filter(o -> j == o.getKey()).findFirst().ifPresent(o -> list.add(o.getValue()));
sigmoids.entrySet().stream().filter(o -> j == o.getKey()).findFirst()
.ifPresent(o -> list.add(o.getValue()));
tanhs.entrySet().stream().filter(o -> j == o.getKey()).findFirst().ifPresent(o -> list.add(o.getValue()));
}
return list;
}
private int sizeOfLayers() {
return this.affines.size() + this.batchNormalizations.size() + this.relus.size() + this.sigmoids.size()
+ this.tanhs.size();
}
public static Dump formJson(String json) {
return gson.fromJson(json, Dump.class);
}
public String toJson() {
return gson.toJson(this);
}
public Network toNetwork() {
Network network = new Network();
network.name(this.name);
network.description(this.description);
network.input(this.input);
network.output(this.output);
network.layers(this.layers());
return network;
}
public <T extends Network> T toNetwork(Class<T> cls)
throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
T t = cls.getConstructor().newInstance();
t.name(this.name);
t.description(this.description);
t.input(this.input);
t.output(this.output);
t.layers(this.layers());
return t;
}
public TrainNetwork toTrainNetwork() {
TrainNetwork network = new TrainNetwork(name, input, input, hiddens) {
@Override
public void init() {
// nothing
}
};
network.name(name).description(description).input(input).output(output).hiddens(hiddens).layers(this.layers());
if (null != this.meanSquareError) {
network.loss(meanSquareError);
} else if (null != this.sigmoidWithMeanSquareError) {
network.loss(this.sigmoidWithMeanSquareError);
} else if (null != this.softmaxWithCrossEntropyError) {
network.loss(this.softmaxWithCrossEntropyError);
}
return network;
}
}
\ No newline at end of file
package com.x.base.core.neural.mlp.dump;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.ByteBuffer;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
import org.apache.commons.codec.binary.Base64;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
import com.x.base.core.neural.tools.FP16;
public class MultilayerPerceptronMatrixAdapter
implements JsonSerializer<MultilayerPerceptronMatrix>, JsonDeserializer<MultilayerPerceptronMatrix> {
private static final String PROPERTY_ROW = "row";
private static final String PROPERTY_COLUMN = "column";
private static final String PROPERTY_DATA = "data";
@Override
public JsonElement serialize(MultilayerPerceptronMatrix matrix, Type typeOfSrc, JsonSerializationContext context) {
float[] arr = new float[matrix.column() * matrix.row()];
final JsonObject jsonObject = new JsonObject();
jsonObject.addProperty(PROPERTY_ROW, matrix.row());
jsonObject.addProperty(PROPERTY_COLUMN, matrix.column());
matrix.visit((r, c, v) -> arr[r * matrix.column() + c] = v);
// byte[] bs = floatToFloatBytes(arr);
byte[] bs = floatToHalfPrecisionBytes(arr);
try {
bs = compress(bs, Deflater.BEST_COMPRESSION, true);
} catch (IOException e) {
e.printStackTrace();
}
jsonObject.addProperty(PROPERTY_DATA, Base64.encodeBase64URLSafeString(bs));
return jsonObject;
}
@Override
public MultilayerPerceptronMatrix deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
int row = jsonObject.get(PROPERTY_ROW).getAsInt();
int column = jsonObject.get(PROPERTY_COLUMN).getAsInt();
MultilayerPerceptronMatrix matrix = new MultilayerPerceptronMatrix(row, column);
byte[] bs = Base64.decodeBase64(jsonObject.get(PROPERTY_DATA).getAsString());
try {
bs = decompress(bs, true);
} catch (DataFormatException e) {
e.printStackTrace();
}
float[] arr = floatFromHalfPrecisionBytes(bs);
matrix.visit((r, c, v) -> matrix.set(r, c, arr[r * matrix.column() + c]));
return matrix;
}
private byte[] floatToHalfPrecisionBytes(float[] arr) {
ByteBuffer buffer = ByteBuffer.allocate(arr.length * 2);
for (int i = 0; i < arr.length; i++) {
buffer.putShort(i * 2, FP16.toHalf(arr[i]));
}
return buffer.array();
}
private float[] floatFromHalfPrecisionBytes(byte[] bytes) {
ByteBuffer buffer = ByteBuffer.wrap(bytes);
int size = bytes.length / 2;
float[] values = new float[size];
for (int i = 0; i < size; i++) {
values[i] = FP16.toFloat(buffer.getShort(i * 2));
}
return values;
}
private byte[] floatToFloatBytes(float[] arr) {
ByteBuffer buffer = ByteBuffer.allocate(arr.length * 4);
for (int i = 0; i < arr.length; i++) {
buffer.putFloat(i * 4, arr[i]);
}
return buffer.array();
}
private float[] floatFormFloatBytes(byte[] bytes) {
ByteBuffer buffer = ByteBuffer.wrap(bytes);
int size = bytes.length / 4;
float[] values = new float[size];
for (int i = 0; i < size; i++) {
values[i] = buffer.getFloat(i * 4);
}
return values;
}
public static byte[] compress(byte[] input, int compressionLevel, boolean gzip) throws IOException {
Deflater compressor = new Deflater(compressionLevel, gzip);
compressor.setInput(input);
compressor.finish();
ByteArrayOutputStream bao = new ByteArrayOutputStream();
byte[] readBuffer = new byte[1024];
int readCount = 0;
while (!compressor.finished()) {
readCount = compressor.deflate(readBuffer);
if (readCount > 0) {
bao.write(readBuffer, 0, readCount);
}
}
compressor.end();
return bao.toByteArray();
}
public static byte[] decompress(byte[] input, boolean gzip) throws DataFormatException {
Inflater decompressor = new Inflater(gzip);
decompressor.setInput(input);
ByteArrayOutputStream bao = new ByteArrayOutputStream();
byte[] readBuffer = new byte[1024];
int readCount = 0;
while (!decompressor.finished()) {
readCount = decompressor.inflate(readBuffer);
if (readCount > 0) {
bao.write(readBuffer, 0, readCount);
}
}
decompressor.end();
return bao.toByteArray();
}
}
package com.x.base.core.neural.mlp.layer;
import java.security.SecureRandom;
import java.util.Objects;
import java.util.Random;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
import com.x.base.core.neural.mlp.optimizer.Adam;
import com.x.base.core.neural.mlp.optimizer.Optimizer;
import com.x.base.core.neural.mlp.optimizer.StochasticGradientDescent;
public class Affine implements Layer {
private String name;
private MultilayerPerceptronMatrix w;
private MultilayerPerceptronMatrix b;
private transient MultilayerPerceptronMatrix dw;
private transient MultilayerPerceptronMatrix db;
private transient MultilayerPerceptronMatrix x;
private int input;
private int output;
private Adam owAdam = new Adam();
private Adam obAdam = new Adam();
private StochasticGradientDescent owStochasticGradientDescent = null;
private StochasticGradientDescent obStochasticGradientDescent = null;
public Affine(String name, int input, int output) {
this.name = name;
this.input = input;
this.output = output;
this.w = new MultilayerPerceptronMatrix(input, output);
this.b = new MultilayerPerceptronMatrix(1, output);
Random random = new SecureRandom();
// 初始化Relu激活函数
float factor = (float) Math.sqrt(2.0 / (float) input);
w.visit((r, c, v) -> w.set(r, c, random.nextFloat() * factor));
b.set(0f);
this.dw = new MultilayerPerceptronMatrix(input, output);
this.db = new MultilayerPerceptronMatrix(1, output);
}
public int input() {
return this.input;
}
public int output() {
return this.output;
}
public Affine(String name, int input, int output, Optimizer ow, Optimizer ob) {
this(name, input, output);
if (ow instanceof StochasticGradientDescent) {
this.owStochasticGradientDescent = (StochasticGradientDescent) ow;
this.owAdam = null;
} else if (ow instanceof Adam) {
this.owStochasticGradientDescent = null;
this.owAdam = (Adam) ow;
}
if (ob instanceof StochasticGradientDescent) {
this.obStochasticGradientDescent = (StochasticGradientDescent) ob;
this.obAdam = null;
} else if (ob instanceof Adam) {
this.obStochasticGradientDescent = null;
this.obAdam = (Adam) ob;
}
}
public MultilayerPerceptronMatrix forward(MultilayerPerceptronMatrix x) {
this.x = x;
MultilayerPerceptronMatrix y = x.dot(this.w);
y.visit((r, c, v) -> y.set(r, c, v + b.get(0, c)));
return y;
}
public MultilayerPerceptronMatrix backward(MultilayerPerceptronMatrix dy) {
this.dw = this.x.transpose().dot(dy);
this.db = dy.sumAsColumn();
return dy.dot(this.w.transpose());
}
public void update() {
this.ow().update(w, dw);
this.ob().update(b, db);
}
public Optimizer ow() {
if (null != owAdam) {
return owAdam;
} else if (null != owStochasticGradientDescent) {
return owStochasticGradientDescent;
} else {
return null;
}
}
public Optimizer ob() {
if (null != obAdam) {
return obAdam;
} else if (null != obStochasticGradientDescent) {
return obStochasticGradientDescent;
} else {
return null;
}
}
public MultilayerPerceptronMatrix w() {
return this.w;
}
public Affine w(MultilayerPerceptronMatrix w) {
this.w = w;
return this;
}
public MultilayerPerceptronMatrix b() {
return this.b;
}
public Affine b(MultilayerPerceptronMatrix b) {
this.b = b;
return this;
}
public MultilayerPerceptronMatrix dw() {
return this.dw;
}
public MultilayerPerceptronMatrix db() {
return this.db;
}
@Override
public String name() {
return Objects.toString(this.name, "");
}
}
package com.x.base.core.neural.mlp.layer;
import com.x.base.core.neural.mlp.MultilayerPerceptronTools;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
import com.x.base.core.neural.mlp.optimizer.Adam;
import com.x.base.core.neural.mlp.optimizer.Optimizer;
import com.x.base.core.neural.mlp.optimizer.StochasticGradientDescent;
public class BatchNormalization implements Layer {
// https://www.pianshen.com/article/638854876/
private Adam optimizerGammaAdam;
private Adam optimizerBetaAdam;
private StochasticGradientDescent optimizerGammaStochasticGradientDescent;
private StochasticGradientDescent optimizerBetaStochasticGradientDescent;
private boolean initialized;
private MultilayerPerceptronMatrix gamma;
private MultilayerPerceptronMatrix beta;
private MultilayerPerceptronMatrix xmu;
private MultilayerPerceptronMatrix variance;
private MultilayerPerceptronMatrix sqrtvar;
private MultilayerPerceptronMatrix ivar;
private MultilayerPerceptronMatrix xhat;
private MultilayerPerceptronMatrix dgamma;
private MultilayerPerceptronMatrix dbeta;
public BatchNormalization() {
this.optimizerGammaAdam = new Adam();
this.optimizerBetaAdam = new Adam();
}
private void init(MultilayerPerceptronMatrix x) {
this.gamma = new MultilayerPerceptronMatrix(1, x.column());
this.gamma.set(1f);
this.beta = new MultilayerPerceptronMatrix(1, x.column());
this.beta.set(0f);
}
public Optimizer optimizerGamma() {
if (null != optimizerGammaAdam) {
return optimizerGammaAdam;
} else if (null != optimizerGammaStochasticGradientDescent) {
return optimizerGammaStochasticGradientDescent;
}
return null;
}
public Optimizer optimizerBeta() {
if (null != optimizerBetaAdam) {
return optimizerBetaAdam;
} else if (null != optimizerBetaStochasticGradientDescent) {
return optimizerBetaStochasticGradientDescent;
}
return null;
}
@Override
public void update() {
this.optimizerGamma().update(this.gamma, this.dgamma);
this.optimizerBeta().update(this.beta, this.dbeta);
}
@Override
public MultilayerPerceptronMatrix forward(MultilayerPerceptronMatrix x) {
// 如果单条数据那么直接通过
if (x.row() < 2) {
return x;
}
if (!this.initialized) {
this.init(x);
this.initialized = true;
}
this.xmu = this.xmu(x);
MultilayerPerceptronMatrix sq = this.sq(this.xmu);
this.variance = this.variance(sq);
this.sqrtvar = this.sqrtvar(variance);
this.ivar = this.ivar(sqrtvar);
this.xhat = this.xhat(this.xmu, this.ivar);
MultilayerPerceptronMatrix gammax = this.gammax(this.gamma, this.xhat);
return this.out(gammax, this.beta);
}
private MultilayerPerceptronMatrix xmu(MultilayerPerceptronMatrix x) {
MultilayerPerceptronMatrix m = x.copy();
for (int i = 0; i < m.column(); i++) {
float mean = m.meanOfColumn(i);
for (int j = 0; j < m.row(); j++) {
m.set(j, i, m.get(j, i) - mean);
}
}
return m;
}
private MultilayerPerceptronMatrix sq(MultilayerPerceptronMatrix xmu) {
MultilayerPerceptronMatrix m = xmu.copy();
m.visit((r, c, v) -> m.set(r, c, v * v));
return m;
}
private MultilayerPerceptronMatrix variance(MultilayerPerceptronMatrix sq) {
MultilayerPerceptronMatrix m = new MultilayerPerceptronMatrix(1, sq.column());
for (int i = 0; i < sq.column(); i++) {
m.set(0, i, sq.meanOfColumn(i));
}
return m;
}
private MultilayerPerceptronMatrix sqrtvar(MultilayerPerceptronMatrix variance) {
MultilayerPerceptronMatrix m = variance.copy();
m.visit((r, c, v) -> m.set(r, c, (float) Math.sqrt(v + MultilayerPerceptronTools.DELTA)));
return m;
}
private MultilayerPerceptronMatrix ivar(MultilayerPerceptronMatrix sqrtvar) {
MultilayerPerceptronMatrix m = sqrtvar.copy();
m.visit((r, c, v) -> m.set(r, c, 1f / v));
return m;
}
private MultilayerPerceptronMatrix xhat(MultilayerPerceptronMatrix xmu, MultilayerPerceptronMatrix ivar) {
MultilayerPerceptronMatrix m = xmu.copy();
for (int i = 0; i < m.column(); i++) {
float v = ivar.get(0, i);
for (int j = 0; j < m.row(); j++) {
m.set(j, i, m.get(j, i) * v);
}
}
return m;
}
private MultilayerPerceptronMatrix gammax(MultilayerPerceptronMatrix gamma, MultilayerPerceptronMatrix xhat) {
MultilayerPerceptronMatrix m = xhat.copy();
for (int i = 0; i < m.column(); i++) {
float v = gamma.get(0, i);
for (int j = 0; j < m.row(); j++) {
m.set(j, i, m.get(j, i) * v);
}
}
return m;
}
private MultilayerPerceptronMatrix out(MultilayerPerceptronMatrix gammax, MultilayerPerceptronMatrix beta) {
for (int i = 0; i < beta.column(); i++) {
float v = beta.get(0, i);
for (int j = 0; j < gammax.row(); j++) {
gammax.set(j, i, gammax.get(j, i) + v);
}
}
return gammax;
}
@Override
public MultilayerPerceptronMatrix backward(MultilayerPerceptronMatrix dout) {
// 如果单条数据那么直接通过
if (dout.row() < 2) {
return dout;
}
this.dbeta = this.dbeta(dout);
MultilayerPerceptronMatrix dgammax = dout.copy();
this.dgamma = this.dgamma(dgammax, this.xhat);
MultilayerPerceptronMatrix dxhat = this.dxhat(dgammax, this.gamma);
MultilayerPerceptronMatrix divar = this.divar(dxhat, this.xmu);
MultilayerPerceptronMatrix dxmu1 = this.dxmu1(dxhat, this.ivar);
MultilayerPerceptronMatrix dsqrtvar = this.dsqrtvar(this.sqrtvar, divar);
MultilayerPerceptronMatrix dvar = this.dvar(this.variance, dsqrtvar);
MultilayerPerceptronMatrix dsq = this.dsq(dout.row(), dout.column(), dvar);
MultilayerPerceptronMatrix dxmu2 = this.dxmu2(this.xmu, dsq);
MultilayerPerceptronMatrix dx1 = this.dx1(dxmu1, dxmu2);
MultilayerPerceptronMatrix dmu = this.dmu(dx1);
MultilayerPerceptronMatrix dx2 = this.dx2(dout.row(), dout.column(), dmu);
return this.dx(dx1, dx2);
}
private MultilayerPerceptronMatrix dbeta(MultilayerPerceptronMatrix dout) {
return dout.sumAsColumn();
}
private MultilayerPerceptronMatrix dgamma(MultilayerPerceptronMatrix dgammax, MultilayerPerceptronMatrix xhat) {
MultilayerPerceptronMatrix m = dgammax.copy();
m.visit((r, c, v) -> m.set(r, c, v * xhat.get(r, c)));
return m.sumAsColumn();
}
private MultilayerPerceptronMatrix dxhat(MultilayerPerceptronMatrix dgammax, MultilayerPerceptronMatrix gamma) {
MultilayerPerceptronMatrix m = dgammax.copy();
for (int i = 0; i < m.column(); i++) {
float value = gamma.get(0, i);
for (int j = 0; j < m.row(); j++) {
m.set(j, i, m.get(j, i) * value);
}
}
return m;
}
private MultilayerPerceptronMatrix divar(MultilayerPerceptronMatrix dxhat, MultilayerPerceptronMatrix xmu) {
MultilayerPerceptronMatrix m = dxhat.copy();
m.visit((r, c, v) -> m.set(r, c, v * xmu.get(r, c)));
return m.sumAsColumn();
}
private MultilayerPerceptronMatrix dxmu1(MultilayerPerceptronMatrix dxhat, MultilayerPerceptronMatrix ivar) {
MultilayerPerceptronMatrix m = dxhat.copy();
for (int i = 0; i < m.column(); i++) {
float value = ivar.get(0, i);
for (int j = 0; j < m.row(); j++) {
m.set(j, i, m.get(j, i) * value);
}
}
return m;
}
private MultilayerPerceptronMatrix dsqrtvar(MultilayerPerceptronMatrix sqrtvar, MultilayerPerceptronMatrix divar) {
MultilayerPerceptronMatrix m = sqrtvar.copy();
m.visit((r, c, v) -> m.set(r, c, (-1.0f / (v * v)) * divar.get(r, c)));
return m;
}
private MultilayerPerceptronMatrix dvar(MultilayerPerceptronMatrix var, MultilayerPerceptronMatrix dsqrtvar) {
MultilayerPerceptronMatrix m = var.copy();
m.visit((r, c, v) -> m.set(r, c, (0.5f / (float) ((Math.sqrt(v + MultilayerPerceptronTools.DELTA))) * dsqrtvar.get(r, c))));
return m;
}
private MultilayerPerceptronMatrix dsq(int n, int d, MultilayerPerceptronMatrix dvar) {
MultilayerPerceptronMatrix m = new MultilayerPerceptronMatrix(n, d);
m.set(1f);
for (int i = 0; i < m.column(); i++) {
float value = dvar.get(0, i);
for (int j = 0; j < m.row(); j++) {
m.set(j, i, (1.0f / (float) n) * value);
}
}
return m;
}
private MultilayerPerceptronMatrix dxmu2(MultilayerPerceptronMatrix xmu, MultilayerPerceptronMatrix dsq) {
MultilayerPerceptronMatrix m = xmu.copy();
m.visit((r, c, v) -> m.set(r, c, 2 * v * dsq.get(r, c)));
return m;
}
private MultilayerPerceptronMatrix dx1(MultilayerPerceptronMatrix dxmu1, MultilayerPerceptronMatrix dxmu2) {
MultilayerPerceptronMatrix m = dxmu1.copy();
m.visit((r, c, v) -> m.set(r, c, v + dxmu2.get(r, c)));
return m;
}
private MultilayerPerceptronMatrix dmu(MultilayerPerceptronMatrix dx1) {
MultilayerPerceptronMatrix m = new MultilayerPerceptronMatrix(1, dx1.column());
for (int i = 0; i < dx1.column(); i++) {
m.set(0, i, -dx1.sumOfColumn(i));
}
return m;
}
private MultilayerPerceptronMatrix dx2(int n, int d, MultilayerPerceptronMatrix dmu) {
MultilayerPerceptronMatrix m = new MultilayerPerceptronMatrix(n, d);
m.set(1.0f);
for (int i = 0; i < m.column(); i++) {
float value = dmu.get(0, i);
for (int j = 0; j < m.row(); j++) {
m.set(j, i, (1.0f / n) * value);
}
}
return m;
}
private MultilayerPerceptronMatrix dx(MultilayerPerceptronMatrix dx1, MultilayerPerceptronMatrix dx2) {
MultilayerPerceptronMatrix m = dx1.copy();
m.visit((r, c, v) -> m.set(r, c, v + dx2.get(r, c)));
return m;
}
}
package com.x.base.core.neural.mlp.layer;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public interface Layer {
MultilayerPerceptronMatrix forward(MultilayerPerceptronMatrix x);
MultilayerPerceptronMatrix backward(MultilayerPerceptronMatrix y);
public default String name() {
return this.getClass().getSimpleName();
}
void update();
}
package com.x.base.core.neural.mlp.layer.activation;
import com.x.base.core.neural.mlp.layer.Layer;
public interface Activation extends Layer {
}
package com.x.base.core.neural.mlp.layer.activation;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class Relu implements Activation {
private transient MultilayerPerceptronMatrix mask;
public MultilayerPerceptronMatrix forward(MultilayerPerceptronMatrix x) {
mask = new MultilayerPerceptronMatrix(x.row(), x.column());
x.visit((int r, int c, float v) -> {
if (v > 0d) {
mask.set(r, c, v);
} else {
mask.set(r, c, 0);
}
});
return mask.copy();
}
public MultilayerPerceptronMatrix backward(MultilayerPerceptronMatrix dy) {
MultilayerPerceptronMatrix dx = new MultilayerPerceptronMatrix(dy.row(), dy.column());
dy.visit((r, c, v) -> {
if (mask.get(r, c) > 0) {
dx.set(r, c, v);
} else {
dx.set(r, c, 0f);
}
});
return dx;
}
@Override
public void update() {
// nothing
}
}
package com.x.base.core.neural.mlp.layer.activation;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class Sigmoid implements Activation {
private MultilayerPerceptronMatrix out;
@Override
public MultilayerPerceptronMatrix forward(MultilayerPerceptronMatrix x) {
MultilayerPerceptronMatrix m = x.copy();
m.visit((r, c, v) -> m.set(r, c, (1f / (1f + (float) Math.exp(-v)))));
out = m.copy();
return m;
}
@Override
public MultilayerPerceptronMatrix backward(MultilayerPerceptronMatrix x) {
MultilayerPerceptronMatrix m = x.copy();
m.visit((r, c, v) -> {
float o = out.get(r, c);
m.set(r, c, v * (1f - o) * o);
});
return m;
}
@Override
public void update() {
// nothing
}
}
package com.x.base.core.neural.mlp.layer.activation;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class Tanh implements Activation {
private MultilayerPerceptronMatrix out;
@Override
public MultilayerPerceptronMatrix forward(MultilayerPerceptronMatrix x) {
MultilayerPerceptronMatrix m = x.copy();
m.visit((r, c, v) -> m.set(r, c, (float) ((Math.pow(Math.E, v) - Math.pow(Math.E, -v))
/ (Math.pow(Math.E, v) + Math.pow(Math.E, -v)))));
out = m.copy();
return m;
}
@Override
public MultilayerPerceptronMatrix backward(MultilayerPerceptronMatrix x) {
MultilayerPerceptronMatrix m = x.copy();
m.visit((r, c, v) -> {
float o = out.get(r, c);
m.set(r, c, v * (1f - o * o));
});
return m;
}
@Override
public void update() {
// nothing
}
}
package com.x.base.core.neural.mlp.loss;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public interface Loss {
MultilayerPerceptronMatrix backward();
float forward(MultilayerPerceptronMatrix x, MultilayerPerceptronMatrix t);
}
package com.x.base.core.neural.mlp.loss;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class MeanSquareError implements Loss {
private transient MultilayerPerceptronMatrix x;
private transient MultilayerPerceptronMatrix t;
public MeanSquareError() {
// nothing
}
// self.x = x
// self.y = y
// return np.sum(np.square(x - y)) / x.size
@Override
public float forward(MultilayerPerceptronMatrix x, MultilayerPerceptronMatrix t) {
this.x = x;
this.t = t;
float s = 0f;
for (int r = 0; r < x.row(); r++) {
for (int c = 0; c < x.column(); c++) {
s += Math.pow(x.get(r, c) - t.get(r, c), 2);
}
}
return s / ((float) x.row());
}
@Override
public MultilayerPerceptronMatrix backward() {
MultilayerPerceptronMatrix m = x.subtract(t);
m.visit((r, c, v) -> m.set(r, c, 2f * (v / (float) x.row())));
return m;
}
}
package com.x.base.core.neural.mlp.loss;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class SigmoidWithMeanSquareError implements Loss {
private transient MultilayerPerceptronMatrix y;
private transient MultilayerPerceptronMatrix t;
public SigmoidWithMeanSquareError() {
// nothing
}
@Override
public float forward(MultilayerPerceptronMatrix x, MultilayerPerceptronMatrix t) {
this.t = t;
this.y = x.sigmoid();
return this.meanSquaredError(this.y, t);
}
@Override
public MultilayerPerceptronMatrix backward() {
float batch = t.row();
MultilayerPerceptronMatrix m = this.y.subtract(this.t);
m.visit((r, c, v) -> m.set(r, c, (v * (1 - v)) / batch));
return m;
}
public float meanSquaredError(MultilayerPerceptronMatrix x, MultilayerPerceptronMatrix t) {
float sum = 0f;
for (int i = 0; i < x.row(); i++) {
float[] xrow = x.row(i);
float[] trow = t.row(i);
for (int j = 0; j < xrow.length; j++) {
sum += Math.pow(xrow[j] - trow[j], 2);
}
}
return sum / (2f * x.row());
}
}
package com.x.base.core.neural.mlp.loss;
import org.apache.commons.lang3.math.NumberUtils;
import com.x.base.core.neural.mlp.MultilayerPerceptronTools;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class SoftmaxWithCrossEntropyError implements Loss {
private transient MultilayerPerceptronMatrix y;
private transient MultilayerPerceptronMatrix t;
public float forward(MultilayerPerceptronMatrix x, MultilayerPerceptronMatrix t) {
this.y = softmaxAsRow(x);
this.t = t;
return crossEntropyError(this.y, t);
}
public MultilayerPerceptronMatrix backward() {
float batch = t.row();
MultilayerPerceptronMatrix m = this.y.subtract(this.t);
m.visit((r, c, v) -> m.set(r, c, v / batch));
return m;
}
private float crossEntropyError(MultilayerPerceptronMatrix x, MultilayerPerceptronMatrix t) {
float sum = 0f;
for (int i = 0; i < x.row(); i++) {
float[] xrow = x.row(i);
float[] trow = t.row(i);
for (int j = 0; j < xrow.length; j++) {
sum += (trow[j] * Math.log(xrow[j] + MultilayerPerceptronTools.DELTA));
}
}
return -(sum / (float) x.row());
}
public MultilayerPerceptronMatrix softmaxAsRow(MultilayerPerceptronMatrix x) {
MultilayerPerceptronMatrix s = new MultilayerPerceptronMatrix(x.row(), x.column());
for (int i = 0; i < x.row(); i++) {
float[] rowData = x.row(i);
float max = NumberUtils.max(rowData);
for (int j = 0; j < rowData.length; j++) {
rowData[j] = (float) Math.exp(rowData[j] - max);
}
float sum = 0f;
for (float d : rowData) {
sum += d;
}
sum += MultilayerPerceptronTools.DELTA;
for (int j = 0; j < rowData.length; j++) {
rowData[j] = rowData[j] / sum;
}
s.row(i, rowData);
}
return s;
}
}
\ No newline at end of file
package com.x.base.core.neural.mlp.matrix;
import java.util.concurrent.CompletableFuture;
import com.google.gson.Gson;
public class MultilayerPerceptronMatrix {
private float[][] data;
private int row;
private int column;
public MultilayerPerceptronMatrix(int row, int column) {
this.row = row;
this.column = column;
this.data = new float[row][column];
}
public int row() {
return this.row;
}
public float[] row(int r) {
return this.data[r].clone();
}
public MultilayerPerceptronMatrix row(int r, float[] row) {
this.data[r] = row;
return this;
}
public int column() {
return this.column;
}
public float[] column(int c) {
float[] v = new float[this.row];
for (int i = 0; i < this.row; i++) {
v[i] = data[i][c];
}
return v;
}
public MultilayerPerceptronMatrix column(int c, float[] column) {
for (int i = 0; i < row(); i++) {
data[i][c] = column[i];
}
return this;
}
public MultilayerPerceptronMatrix dot1(MultilayerPerceptronMatrix y) {
MultilayerPerceptronMatrix result = new MultilayerPerceptronMatrix(this.row, y.column);
for (int r = 0; r < result.row; r++) {
for (int c = 0; c < result.column; c++) {
float v = 0f;
for (int i = 0; i < this.column; i++) {
v += this.get(r, i) * y.get(i, c);
}
result.set(r, c, v);
}
}
return result;
}
public MultilayerPerceptronMatrix dot(MultilayerPerceptronMatrix y) {
MultilayerPerceptronMatrix result = new MultilayerPerceptronMatrix(this.row, y.column);
@SuppressWarnings("unchecked")
CompletableFuture<Void>[] futrues = new CompletableFuture[result.row()];
for (int r = 0; r < result.row(); r++) {
futrues[r] = future(r, y, result);
}
try {
CompletableFuture.allOf(futrues).get();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
private CompletableFuture<Void> future(int r, MultilayerPerceptronMatrix y, MultilayerPerceptronMatrix result) {
return CompletableFuture.runAsync(() -> {
for (int c = 0; c < result.column; c++) {
float v = 0f;
for (int i = 0; i < this.column; i++) {
v += this.get(r, i) * y.get(i, c);
}
result.set(r, c, v);
}
});
}
public float get(int r, int c) {
return data[r][c];
}
public MultilayerPerceptronMatrix set(int r, int c, float v) {
data[r][c] = v;
return this;
}
public MultilayerPerceptronMatrix set(float v) {
for (int i = 0; i < row(); i++) {
for (int j = 0; j < column(); j++) {
data[i][j] = v;
}
}
return this;
}
public MultilayerPerceptronMatrix copy() {
MultilayerPerceptronMatrix n = new MultilayerPerceptronMatrix(this.row, this.column);
for (int r = 0; r < row(); r++) {
for (int c = 0; c < column(); c++) {
n.set(r, c, this.get(r, c));
}
}
return n;
}
public MultilayerPerceptronMatrix transpose() {
MultilayerPerceptronMatrix n = new MultilayerPerceptronMatrix(this.column, this.row);
for (int i = 0; i < row(); i++) {
for (int j = 0; j < column(); j++) {
n.set(j, i, this.get(i, j));
}
}
return n;
}
public MultilayerPerceptronMatrix sumAsColumn() {
MultilayerPerceptronMatrix m = new MultilayerPerceptronMatrix(1, this.column);
for (int i = 0; i < this.column; i++) {
float v = 0f;
for (int j = 0; j < this.row; j++) {
v += this.get(j, i);
}
m.set(0, i, v);
}
return m;
}
public void visit(Visitor visitor) {
for (int i = 0; i < this.row; i++) {
for (int j = 0; j < this.column; j++) {
visitor.visit(i, j, this.get(i, j));
}
}
}
public float meanOfColumn(int c) {
return this.sumOfColumn(c) / (float) this.row;
}
public float sumOfColumn(int c) {
float v = 0;
for (int i = 0; i < this.row; i++) {
v += data[i][c];
}
return v;
}
public MultilayerPerceptronMatrix sigmoid() {
MultilayerPerceptronMatrix m = this.copy();
m.visit((r, c, v) -> m.set(r, c, (float) (1 / (1 + Math.exp(-v)))));
return m;
}
public MultilayerPerceptronMatrix add(MultilayerPerceptronMatrix y) {
MultilayerPerceptronMatrix m = new MultilayerPerceptronMatrix(row(), column());
this.visit((r, c, v) -> m.set(r, c, this.get(r, c) + y.get(r, c)));
return m;
}
public MultilayerPerceptronMatrix subtract(MultilayerPerceptronMatrix y) {
MultilayerPerceptronMatrix m = new MultilayerPerceptronMatrix(row(), column());
this.visit((r, c, v) -> m.set(r, c, this.get(r, c) - y.get(r, c)));
return m;
}
@FunctionalInterface
public interface Visitor {
void visit(int row, int column, float value);
}
@Override
public String toString() {
return new Gson().toJson(this);
}
}
package com.x.base.core.neural.mlp.optimizer;
import com.x.base.core.neural.mlp.MultilayerPerceptronTools;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class Adam implements Optimizer {
private float learningRate = 0.001f;
private float beta1 = 0.9f;
private float beta2 = 0.999f;
private int iter = 0;
private MultilayerPerceptronMatrix m;
private MultilayerPerceptronMatrix v;
public Adam() {
}
public Adam(float learningRate, float beta1, float beta2) {
this.learningRate = learningRate;
this.beta1 = beta1;
this.beta2 = beta2;
}
public int iter() {
return this.iter;
}
public Adam iter(int iter) {
this.iter = iter;
return this;
}
public double learningRate() {
return this.learningRate;
}
public Adam learningRate(float learningRate) {
this.learningRate = learningRate;
return this;
}
public void update(MultilayerPerceptronMatrix x, MultilayerPerceptronMatrix dx) {
if (m == null) {
m = new MultilayerPerceptronMatrix(x.row(), x.column());
v = new MultilayerPerceptronMatrix(x.row(), x.column());
}
iter++;
float temp = (float) (learningRate * Math.sqrt(1f - Math.pow(beta2, iter)) / (1f - Math.pow(beta1, iter)));
x.visit((r, c, value) -> {
float d = dx.get(r, c);
float mv = m.get(r, c);
float vv = v.get(r, c);
mv = mv + (1f - beta1) * (d - mv);
vv = vv + (float) ((1f - beta2) * (Math.pow(d, 2) - vv));
m.set(r, c, mv);
v.set(r, c, vv);
x.set(r, c, (value - (float) ((temp * mv) / (Math.sqrt(vv) + MultilayerPerceptronTools.DELTA))));
});
}
public static class Param {
private float learningRate = 0.001f;
private float beta1 = 0.9f;
private float beta2 = 0.999f;
public float getLearningRate() {
return learningRate;
}
public void setLearningRate(float learningRate) {
this.learningRate = learningRate;
}
public float getBeta1() {
return beta1;
}
public void setBeta1(float beta1) {
this.beta1 = beta1;
}
public float getBeta2() {
return beta2;
}
public void setBeta2(float beta2) {
this.beta2 = beta2;
}
}
}
\ No newline at end of file
package com.x.base.core.neural.mlp.optimizer;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public interface Optimizer {
public void update(MultilayerPerceptronMatrix x, MultilayerPerceptronMatrix dx);
}
package com.x.base.core.neural.mlp.optimizer;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class StochasticGradientDescent implements Optimizer {
private float learningRate = 0.001f;
public StochasticGradientDescent() {
}
public StochasticGradientDescent(float learningRate) {
this.learningRate = learningRate;
}
@Override
public void update(MultilayerPerceptronMatrix x, MultilayerPerceptronMatrix dx) {
x.visit((r, c, v) -> x.set(r, c, v - (learningRate * dx.get(r, c))));
}
}
package com.x.base.core.neural.mlp.train;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import com.x.base.core.neural.mlp.MultilayerPerceptronTools;
import com.x.base.core.neural.mlp.layer.Affine;
import com.x.base.core.neural.mlp.layer.Layer;
import com.x.base.core.neural.mlp.layer.activation.Relu;
import com.x.base.core.neural.mlp.loss.Loss;
import com.x.base.core.neural.mlp.loss.SoftmaxWithCrossEntropyError;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public abstract class TrainNetwork {
private String name;
private String description;
private int input;
private int output;
private int[] hiddens;
private List<Layer> layers = new ArrayList<>();
private Loss loss = new SoftmaxWithCrossEntropyError();
public String name() {
return Objects.toString(this.name, "");
}
public TrainNetwork name(String name) {
this.name = name;
return this;
}
public String description() {
return Objects.toString(this.description, "");
}
public TrainNetwork description(String description) {
this.description = description;
return this;
}
public int input() {
return this.input;
}
public TrainNetwork input(int input) {
this.input = input;
return this;
}
public int output() {
return this.output;
}
public TrainNetwork output(int output) {
this.output = output;
return this;
}
public int[] hiddens() {
return this.hiddens;
}
public TrainNetwork hiddens(int[] hiddens) {
this.hiddens = hiddens;
return this;
}
public List<Layer> layers() {
return this.layers;
}
public void layers(List<Layer> list) {
this.layers = list;
}
public Loss loss() {
return this.loss;
}
public TrainNetwork loss(Loss loss) {
this.loss = loss;
return this;
}
public TrainNetwork() {
}
public TrainNetwork(String name, int input, int output, int... hiddens) {
this.name = name;
this.input = input;
this.output = output;
this.hiddens = hiddens;
this.init();
}
public abstract void init();
public TrainNetwork defaultInit() {
int[] arr = new int[hiddens.length + 2];
arr[0] = input;
for (int i = 0; i < hiddens.length; i++) {
arr[i + 1] = hiddens[i];
}
arr[hiddens.length + 1] = output;
for (int i = 0; i < arr.length - 1; i++) {
Affine affine = new Affine("affine" + String.format("%02d", i), arr[i], arr[i + 1]);
layers.add(affine);
layers.add(new Relu());
}
return this;
}
public TrainNetwork initParameter() {
return this;
}
public List<Affine> listAffine() {
List<Affine> list = new ArrayList<>();
for (Layer layer : this.layers) {
if (layer instanceof Affine) {
list.add((Affine) layer);
}
}
return list;
}
public MultilayerPerceptronMatrix predict(MultilayerPerceptronMatrix x) {
for (Layer layer : layers) {
x = layer.forward(x);
}
return x;
}
public TrainNetwork gradient(MultilayerPerceptronMatrix dy) {
for (int i = layers.size() - 1; i >= 0; i--) {
dy = layers.get(i).backward(dy);
}
return this;
}
public float accuracy(MultilayerPerceptronMatrix x, MultilayerPerceptronMatrix t) {
MultilayerPerceptronMatrix y = this.predict(x);
int match = 0;
for (int r = 0; r < y.row(); r++) {
if (MultilayerPerceptronTools.argmax(y.row(r)) == MultilayerPerceptronTools.argmax(t.row(r))) {
match++;
}
}
return (match / (float) y.row());
}
public void update() {
for (Layer layer : this.layers()) {
layer.update();
}
}
}
package com.x.base.core.neural.mlp.train.sample.mnist;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.zip.GZIPInputStream;
import org.apache.commons.io.IOUtils;
import com.x.base.core.neural.mlp.MultilayerPerceptronTools;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class MNIST {
private MNIST() {
}
public static MNIST init() throws URISyntaxException, IOException {
load(MNIST_TRAIN_IMAGES_NAME);
load(MNIST_TRAIN_LABELS_NAME);
load(MNIST_TEST_IMAGES_NAME);
load(MNIST_TEST_LABELS_NAME);
return new MNIST();
}
private static void load(String name) throws URISyntaxException, IOException {
// Path p = Paths.get(ClassLoader.getSystemResource(Paths.get(MNIST_DIR,
// name).toString()).toURI());
Path p = Paths.get(MNIST_DIR, name);
if (!Files.exists(p)) {
byte[] bytes = IOUtils.toByteArray(new URL(URL_BASE + MNIST_TRAIN_IMAGES_NAME + ".gz"));
try (GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(bytes))) {
Files.write(p, IOUtils.toByteArray(gis));
}
}
}
public static final String URL_BASE = "http://yann.lecun.com/exdb/mnist/";
public static final String MNIST_DIR = "MNIST_data";
public static final String MNIST_TRAIN_IMAGES_NAME = "train-images.idx3-ubyte";
public static final String MNIST_TRAIN_LABELS_NAME = "train-labels.idx1-ubyte";
public static final String MNIST_TEST_IMAGES_NAME = "t10k-images.idx3-ubyte";
public static final String MNIST_TEST_LABELS_NAME = "t10k-labels.idx1-ubyte";
public DataSet trainDataSet(boolean normalize) throws IOException, URISyntaxException {
return dataSet(Paths.get(MNIST_DIR, MNIST_TRAIN_IMAGES_NAME), Paths.get(MNIST_DIR, MNIST_TRAIN_LABELS_NAME),
60000, normalize);
}
public DataSet testDataSet(boolean normalize) throws IOException, URISyntaxException {
return dataSet(Paths.get(MNIST_DIR, MNIST_TEST_IMAGES_NAME), Paths.get(MNIST_DIR, MNIST_TEST_LABELS_NAME),
10000, normalize);
}
private DataSet dataSet(Path imagePath, Path labelPath, int count, boolean normalize)
throws IOException, URISyntaxException {
DataSet set = new DataSet();
byte[] imageBytes = read(imagePath);
byte[] labelBytes = read(labelPath);
for (int i = 0; i < count; i++) {
float[] input = new float[28 * 28];
int n = 0;
for (int j = i * 28 * 28 + 16; j < (i + 1) * 28 * 28 + 16; j++) {
input[n++] = Byte.toUnsignedInt(imageBytes[j]);
}
if (normalize) {
for (int j = 0; j < input.length; j++) {
input[j] = input[j] / 255f;
}
}
Data data = new Data();
data.input = input;
float[] label = new float[10];
Arrays.fill(label, 0f);
label[Byte.toUnsignedInt(labelBytes[8 + i])] = 1f;
data.label = label;
set.add(data);
}
return set;
}
public class DataSet extends ArrayList<Data> implements com.x.base.core.neural.DataSet<Data> {
private static final long serialVersionUID = 1L;
private Random random = new SecureRandom();
public DataSet sub(int fromIndex, int toIndex) {
DataSet set = new DataSet();
for (int i = fromIndex; i < toIndex; i++) {
set.add(this.get(i));
}
return set;
}
public DataSet filter(int label) {
DataSet set = new DataSet();
for (Data data : this) {
if (MultilayerPerceptronTools.argmax(data.label) == label) {
set.add(data);
}
}
return set;
}
public DataSet choice(int... arr) {
DataSet set = new DataSet();
for (int i : arr) {
set.add(this.get(i));
}
return set;
}
public DataSet choice(List<Integer> list) {
DataSet set = new DataSet();
for (int i : list) {
set.add(this.get(i));
}
return set;
}
public DataSet randomChoice(int size) {
DataSet set = new DataSet();
random.ints(size, 0, this.size()).forEach(o -> set.add(this.get(o)));
return set;
}
public MultilayerPerceptronMatrix input() {
MultilayerPerceptronMatrix x = new MultilayerPerceptronMatrix(this.size(), 28 * 28);
for (int i = 0; i < x.row(); i++) {
x.row(i, this.get(i).input);
}
return x;
}
public MultilayerPerceptronMatrix label() {
MultilayerPerceptronMatrix x = new MultilayerPerceptronMatrix(this.size(), 10);
for (int i = 0; i < x.row(); i++) {
x.row(i, this.get(i).label);
}
return x;
}
}
public static class Data implements com.x.base.core.neural.mlp.Data {
protected float[] input;
protected float[] label;
public float[] input() {
return input;
}
public float[] label() {
return label;
}
@Override
public int inputSize() {
return 28 * 28;
}
@Override
public int labelSize() {
return 10;
}
}
private static byte[] read(Path path) throws IOException, URISyntaxException {
byte[] bs = null;
try (InputStream in = Files.newInputStream(path)) {
bs = IOUtils.toByteArray(in);
}
return bs;
}
}
package com.x.base.core.neural.mlp.train.sample.mnist;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import javax.imageio.ImageIO;
import com.google.gson.Gson;
import com.x.base.core.neural.mlp.MultilayerPerceptronTools;
import com.x.base.core.neural.mlp.Network;
import com.x.base.core.neural.mlp.layer.Affine;
import com.x.base.core.neural.mlp.layer.BatchNormalization;
import com.x.base.core.neural.mlp.layer.Layer;
import com.x.base.core.neural.mlp.layer.activation.Relu;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
import com.x.base.core.neural.mlp.train.sample.mnist.MNIST.DataSet;
public class MNISTPerspectiveNetwork extends Network {
public Affine[] affines() {
List<Affine> list = new ArrayList<>();
for (Layer layer : this.layers()) {
if (layer instanceof Affine) {
list.add((Affine) layer);
}
}
return list.toArray(new Affine[] {});
}
public BatchNormalization[] batchNormalizations() {
List<BatchNormalization> list = new ArrayList<>();
for (Layer layer : this.layers()) {
if (layer instanceof BatchNormalization) {
list.add((BatchNormalization) layer);
}
}
return list.toArray(new BatchNormalization[] {});
}
public Relu[] relus() {
List<Relu> list = new ArrayList<>();
for (Layer layer : this.layers()) {
if (layer instanceof Relu) {
list.add((Relu) layer);
}
}
return list.toArray(new Relu[] {});
}
@Override
public MultilayerPerceptronMatrix predict(MultilayerPerceptronMatrix x) {
for (Layer layer : layers()) {
x = layer.forward(x);
if (layer == relus()[0]) {
perspectiveRelu(x, 0, 28, 28);
} else if (layer == relus()[1]) {
perspectiveRelu(x, 1, 10, 10);
}
// } else if (layer == relus()[2]) {
// perspectiveRelu(x, 2);
// }
}
return x;
}
private void perspectiveRelu(MultilayerPerceptronMatrix y, int position, int rowSize, int columnSize) {
float[] values = y.row(0);
try {
float[] combineValues = new float[rowSize * columnSize];
for (int i = 0; i < combineValues.length; i++) {
combineValues[i] = 0f;
}
for (int i = 0; i < values.length; i++) {
if (values[i] > 0) {
float[] ws = affines()[position].w().column(i);
String json = (new Gson()).toJson(ws);
Files.writeString(Paths.get("perspective", "w" + position + "_" + i + ".json"), json);
BufferedImage image = new BufferedImage(rowSize, columnSize, BufferedImage.TYPE_INT_RGB);
ws = MultilayerPerceptronTools.normalize0to1(ws);
for (int column = 0; column < columnSize; column++) {
for (int row = 0; row < rowSize; row++) {
if (ws[column * columnSize + row] * 255 > 200d) {
combineValues[column * columnSize + row] = combineValues[column * columnSize + row]
+ ws[column * columnSize + row];
}
int value = (int) (ws[column * columnSize + row] * 255d);
if (value < 210) {
value = 0;
}
Color color = new Color(value, value, value);
image.setRGB(row, column, color.getRGB());
}
}
File out = new File("perspective/w" + position + "_" + i + ".jpg");
ImageIO.write(image, "jpg", out);
}
}
BufferedImage combine = new BufferedImage(rowSize, columnSize, BufferedImage.TYPE_INT_RGB);
combineValues = MultilayerPerceptronTools.normalize0to1(combineValues);
for (int column = 0; column < columnSize; column++) {
for (int row = 0; row < rowSize; row++) {
int v = (int) (combineValues[column * columnSize + row] * 255d);
Color color = new Color(v, v, v);
combine.setRGB(row, column, color.getRGB());
}
}
File out = new File("perspective/w" + position + "_combine.jpg");
ImageIO.write(combine, "jpg", out);
} catch (IOException e) {
e.printStackTrace();
}
}
public void perspective(DataSet dataSet) {
for (int i = 0; i < dataSet.size(); i++) {
DataSet ds = dataSet.choice(i);
MultilayerPerceptronMatrix y = this.predict(ds.input());
int p = MultilayerPerceptronTools.argmax(y.row(0));
int l = MultilayerPerceptronTools.argmax(ds.label().row(0));
System.out.println("predict:" + p + ", label:" + l);
}
}
}
package com.x.base.core.neural.mlp.train.sample.mnist;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import com.x.base.core.neural.mlp.dump.Dump;
import com.x.base.core.neural.mlp.layer.Affine;
import com.x.base.core.neural.mlp.layer.activation.Relu;
import com.x.base.core.neural.mlp.loss.SoftmaxWithCrossEntropyError;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
import com.x.base.core.neural.mlp.train.TrainNetwork;
import com.x.base.core.neural.mlp.train.sample.mnist.MNIST.DataSet;
public class MnistTrainNetwork {
public static void main(String... args) throws Exception {
TrainNetwork network = new TrainNetwork("MNIST", 784, 10, 100, 100) {
@Override
public void init() {
int[] arr = new int[hiddens().length + 2];
arr[0] = input();
for (int i = 0; i < hiddens().length; i++) {
arr[i + 1] = hiddens()[i];
}
arr[hiddens().length + 1] = output();
for (int i = 0; i < arr.length - 1; i++) {
Affine affine = new Affine("affine" + String.format("%02d", i), arr[i], arr[i + 1]);
layers().add(affine);
// layers().add(new BatchNormalization());
if (i != (arr.length - 2)) {
layers().add(new Relu());
}
}
loss(new SoftmaxWithCrossEntropyError());
}
};
MNIST mnist = MNIST.init();
DataSet trainSet = mnist.trainDataSet(true);
DataSet testSet = mnist.testDataSet(true);
for (int i = 0; i < 600 * 10000; i++) {
DataSet set = trainSet.randomChoice(100);
MultilayerPerceptronMatrix y = network.predict(set.input());
network.loss().forward(y, set.label());
MultilayerPerceptronMatrix dy = network.loss().backward();
network.gradient(dy).update();
if (i % 600 == 0) {
DumpThread thread = new DumpThread(network, testSet);
thread.start();
}
}
}
public static class DumpThread extends Thread {
private TrainNetwork trainNetwork;
private DataSet testSet;
public DumpThread(TrainNetwork trainNetwork, DataSet testSet) {
this.trainNetwork = trainNetwork;
this.testSet = testSet;
}
@Override
public void run() {
try {
DataSet test = testSet.randomChoice(100);
double loss = trainNetwork.loss().forward(trainNetwork.predict(test.input()), test.label());
double accuracy = trainNetwork.accuracy(test.input(), test.label());
System.out.println(
trainNetwork.name() + "(" + (new Date()) + ")-> loss:" + loss + ", accuracy:" + accuracy + ".");
Dump dump = new Dump(trainNetwork);
Path dir = Paths.get("dump");
Files.createDirectories(dir);
Path path = dir.resolve(trainNetwork.name() + ".json");
Files.writeString(path, dump.toJson(), StandardCharsets.UTF_8);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
package com.x.base.core.neural.mlp.train.sample.mnist;
import org.apache.commons.math3.linear.MatrixUtils;
import org.apache.commons.math3.linear.RealMatrix;
public class Test2 {
public static void main(String... args) throws Exception {
RealMatrix w = MatrixUtils.createRealMatrix(8, 6);
w.setColumn(0, new double[] { 0.00f, 0.01f, 0.02f, 0.03f, 0.04f, 0.05f, 0.06f, 0.07f });
w.setColumn(1, new double[] { 0.10f, 0.11f, 0.12f, 0.13f, 0.14f, 0.15f, 0.16f, 0.17f });
w.setColumn(2, new double[] { 0.20f, 0.21f, 0.22f, 0.23f, 0.24f, 0.25f, 0.26f, 0.27f });
w.setColumn(3, new double[] { 0.30f, 0.31f, 0.32f, 0.33f, 0.34f, 0.35f, 0.36f, 0.37f });
w.setColumn(4, new double[] { 0.40f, 0.41f, 0.42f, 0.43f, 0.44f, 0.45f, 0.46f, 0.47f });
w.setColumn(5, new double[] { 0.50f, 0.51f, 0.52f, 0.53f, 0.54f, 0.55f, 0.56f, 0.57f });
RealMatrix input = MatrixUtils.createRealMatrix(1, 8);
input.setRow(0, new double[] { 1d, 2d, 3d, 4d, 5d, 6d, 7d, 8d });
System.out.println(input.multiply(w));
}
}
package com.x.base.core.neural.mlp.train.sample.mnist;
import com.x.base.core.neural.mlp.layer.Affine;
import com.x.base.core.neural.mlp.layer.BatchNormalization;
import com.x.base.core.neural.mlp.layer.activation.Relu;
import com.x.base.core.neural.mlp.loss.Loss;
import com.x.base.core.neural.mlp.loss.MeanSquareError;
import com.x.base.core.neural.mlp.matrix.MultilayerPerceptronMatrix;
public class TestCheckGradient {
private static Affine affine1;
private static Affine affine2;
private static Relu relu1 = new Relu();
private static Relu relu2 = new Relu();
private static BatchNormalization batchNormalization1 = new BatchNormalization();
private static BatchNormalization batchNormalization2 = new BatchNormalization();
private static Loss loss1 = new MeanSquareError();
private static Loss loss2 = new MeanSquareError();
private static MultilayerPerceptronMatrix w = new MultilayerPerceptronMatrix(8, 6);
private static MultilayerPerceptronMatrix b = new MultilayerPerceptronMatrix(1, 6);
private static MultilayerPerceptronMatrix input = new MultilayerPerceptronMatrix(1, 8);
private static MultilayerPerceptronMatrix output = new MultilayerPerceptronMatrix(1, 6);
public static void main(String... args) throws Exception {
w.column(0, new float[] { 0.00f, 0.01f, 0.02f, 0.03f, 0.04f, 0.05f, 0.06f, 0.07f });
w.column(1, new float[] { 0.10f, 0.11f, 0.12f, 0.13f, 0.14f, 0.15f, 0.16f, 0.17f });
w.column(2, new float[] { 0.20f, 0.21f, 0.22f, 0.23f, 0.24f, 0.25f, 0.26f, 0.27f });
w.column(3, new float[] { 0.30f, 0.31f, 0.32f, 0.33f, 0.34f, 0.35f, 0.36f, 0.37f });
w.column(4, new float[] { 0.40f, 0.41f, 0.42f, 0.43f, 0.44f, 0.45f, 0.46f, 0.47f });
w.column(5, new float[] { 0.50f, 0.51f, 0.52f, 0.53f, 0.54f, 0.55f, 0.56f, 0.57f });
b.row(0, new float[] { 0f, 0f, 0f, 0f, 0f, 0f });
input.row(0, new float[] { 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f });
output.row(0, new float[] { 0f, 1f, 0f, 0f, 0f, 0f });
affine1 = new Affine("gradient", 8, 6);
affine2 = new Affine("numericalGradient", 8, 6);
affine1.w(w.copy());
affine1.b(b.copy());
affine2.w(w.copy());
affine2.b(b.copy());
MultilayerPerceptronMatrix y1 = affine1.forward(input);
MultilayerPerceptronMatrix y12 = batchNormalization1.forward(y1);
loss1.forward(y12, output);
affine1.backward(batchNormalization1.backward(loss1.backward()));
numericalGradient(affine2, batchNormalization2, loss2, affine2.w(), affine2.dw(), input, output);
System.out.println("gradient:" + sum(affine1.dw()) + ", numericalGradient:" + sum(affine2.dw()));
}
private static double sum(MultilayerPerceptronMatrix x) {
double s = 0f;
for (int r = 0; r < x.row(); r++) {
for (int c = 0; c < x.column(); c++) {
s += x.get(r, c);
}
}
return s;
}
private static void numericalGradient(Affine affine, BatchNormalization batchNormalization, Loss loss,
MultilayerPerceptronMatrix m, MultilayerPerceptronMatrix d, MultilayerPerceptronMatrix x,
MultilayerPerceptronMatrix t) {
for (int i = 0; i < m.row(); i++) {
for (int j = 0; j < m.column(); j++) {
float original = m.get(i, j);
m.set(i, j, original + (1e-4f));
float f1 = loss.forward(batchNormalization.forward(affine.forward(x)), t);
m.set(i, j, original - (1e-4f));
float f2 = loss.forward(batchNormalization.forward(affine.forward(x)), t);
System.out.println(original + "->" + f1 + ":" + f2);
d.set(i, j, (f1 - f2) / (2 * (1e-4f)));
m.set(i, j, original);
}
}
}
}
package com.x.base.core.neural.mlp.train.sample.mnist;
import java.nio.file.Files;
import java.nio.file.Paths;
import com.x.base.core.neural.mlp.dump.Dump;
import com.x.base.core.neural.mlp.train.sample.mnist.MNIST.DataSet;
public class TestMNISTPerspectiveNetwork {
public static void main(String... args) throws Exception {
String json = Files.readString(Paths.get("dump", "MNIST.json"));
Dump dump = Dump.formJson(json);
MNISTPerspectiveNetwork network = dump.toNetwork(MNISTPerspectiveNetwork.class);
DataSet trainSet = MNIST.init().trainDataSet(true);
network.perspective(trainSet.choice(4));
}
}
......@@ -348,9 +348,9 @@ class ActionProcessing extends BaseAction {
try (EntityManagerContainer emc = EntityManagerContainerFactory.instance().create()) {
Business business = new Business(emc);
for (Task o : empowerTasks) {
Record r = RecordBuilder.ofTaskEmpower(o, task.getEmpowerFromIdentity(),
business.organization().person().getWithIdentity(task.getEmpowerFromIdentity()),
business.organization().unit().getWithIdentity(task.getEmpowerFromIdentity()));
Record r = RecordBuilder.ofTaskEmpower(o,
business.organization().person().getWithIdentity(o.getEmpowerFromIdentity()),
business.organization().unit().getWithIdentity(o.getEmpowerFromIdentity()));
records.add(r);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册