将神经网络部分重构成一个包

This commit is contained in:
qing committed 2026-08-25 11:34:11 +08:00
1 parent d9b2359d25
commit 04f1375b45
5 files changed
+2 -376

No files matched your search

+1 -1
View File
@@ -21,7 +21,7 @@
spi4cj = "1.0.0"
#lua4cj = { git = "https://gitcode.com/qingss0/lua4cj.git", branch="main" }
lua4cj = "1.0.0"
"cangjie_tpc::matrix4cj" = "1.0.5"
nn4cj = { git = "https://gitcode.com/qingss0/nn4cj.git", branch="main"}
# 这种x86机器人开发板感觉不经常使用,我没用过,不知道里面的i2c调用是不是参数是一样的
[target.x86_64-unknown-linux-gnu]
[target.x86_64-unknown-linux-gnu.dependencies]
+1 -1
View File
@@ -13,8 +13,8 @@ import std.io.*
import std.fs.*
import stdx.encoding.json.*
import bot4cj.hd.*
import bot4cj.math.*
import ctype4cj.*
import nn4cj.*
/* ---- 神经网络输出路径 ---- */
var NNS_OUT: Option<String> = None
-192
View File
@@ -1,192 +0,0 @@
package bot4cj.math
import std.math.*
import cangjie_tpc::matrix4cj.Matrix
/*
* -------- Activation Functions 激活函数族 --------
* TODO(20260516-qing): 增加序列化、反序列化工厂函数
* TODO(20260825-qing): 改为使用matrix4cj库
*/
public enum Af {
None | Sigmoid | ReLU | Leaky_ReLU(Float64) | Tanh
/* -------- 执行激活函数 -------- */
public func forward(x: Matrix) {
match(this) {
case None => none(x)
case Sigmoid => sigmoid(x)
case ReLU => relu(x)
case Leaky_ReLU(alpha) => leaky_relu(x, alpha)
case Tanh => tanh(x)
}
}
/* -------- 激活函数的导数 -------- */
public func backward(y: Matrix) {
match(this) {
case None => none_dot(y)
case Sigmoid => sigmoid_dot(y)
case ReLU => relu_dot(y)
case Leaky_ReLU(alpha) => leaky_relu_dot(y, alpha)
case Tanh => tanh_dot(y)
}
}
/* -------- 序列化与反序列化 -------- */
public func get_type(): String {
return match(this) {
case None => "None"
case Sigmoid => "Sigmoid"
case ReLU => "ReLU"
case Leaky_ReLU(alpha) => "Leaky_ReLU"
case Tanh => "Tanh"
}
}
/* NOTE(20260825-qing): 这个函数在序列化时可能会有用 */
public func get_value(): Float64 {
return match(this) {
case None => Float64(0.0f64)
case Sigmoid => Float64(0.0f64)
case ReLU => Float64(0.0f64)
case Leaky_ReLU(alpha) => alpha
case Tanh => Float64(0.0f64)
case Softmax => Float64(0.0f64)
}
}
/* -------- 空的激活函数 -------- */
private static func none(M: Matrix) {
return M.copy()
}
/* -------- 空激活函数的导数 -------- */
private static func none_dot(M: Matrix) {
let row = M.getRowDimension()
let col = M.getColumnDimension()
return Matrix(row, col, 1.0f64)
}
/* -------- S型生长曲线 -------- */
private static func sigmoid(M: Matrix) {
let row = M.getRowDimension()
let col = M.getColumnDimension()
let N = Matrix(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x1 = M.get(r, c)
let x2 = 1.0f64 / (1.0f64 + exp(-x1))
N.set(r, c, x2)
}
}
return N
}
/* -------- S型生长曲线的导数 -------- */
private static func sigmoid_dot(M: Matrix) {
let row = M.getRowDimension()
let col = M.getColumnDimension()
let N = Matrix(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x1 = M.get(r, c)
let x2 = 1.0f64 / (1.0f64 + exp(-x1))
let x3 = x2 * (1.0f64 - x2)
N.set(r, c, x3)
}
}
return N
}
/* -------- 线性整流单元 -------- */
private static func relu(M: Matrix) {
let row = M.getRowDimension()
let col = M.getColumnDimension()
let N = Matrix(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x1 = M.get(r, c)
let x2 =if (x1 > 0.0f64) { x1 } else{ 0.0f64 }
N.set(r, c, x2)
}
}
return N
}
/* -------- 线性整流单元的导数 -------- */
private static func relu_dot(M: Matrix) {
let row = M.getRowDimension()
let col = M.getColumnDimension()
let N = Matrix(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x1 = M.get(r, c)
let x2 =if (x1 > 0.0f64) { 1.0f64 } else{ 0.0f64 }
N.set(r, c, x2)
}
}
return N
}
/* -------- 带泄露的线性整流单元 -------- */
private static func leaky_relu(M: Matrix, alpha: Float64) {
let row = M.getRowDimension()
let col = M.getColumnDimension()
let N = Matrix(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x1 = M.get(r, c)
let x2 =if (x1 > 0.0f64) { x1 } else{ x1 * alpha }
N.set(r, c, x2)
}
}
return N
}
/* -------- 带泄露的整流单元导数 -------- */
private static func leaky_relu_dot(M: Matrix, alpha: Float64) {
let row = M.getRowDimension()
let col = M.getColumnDimension()
let N = Matrix(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x1 = M.get(r, c)
let x2 =if (x1 > 0.0f64) { 1.0f64 } else{ alpha }
N.set(r, c, x2)
}
}
return N
}
/* -------- 双曲正切函数 --------*/
private static func tanh(M: Matrix) {
let row = M.getRowDimension()
let col = M.getColumnDimension()
let N = Matrix(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x1 = M.get(r, c)
let x2 = tanh(x1)
N.set(r, c, x2)
}
}
return N
}
/* -------- 双曲正切函数的导数 -------- */
private static func tanh_dot(M: Matrix) {
let row = M.getRowDimension()
let col = M.getColumnDimension()
let N = Matrix(row, col)
for (r in 0..row) {
for (c in 0..col) {
let y_val = M.get(r, c)
let rea = 1.0f64 - y_val * y_val
N.set(r, c, rea)
}
}
return N
}
}
-28
View File
@@ -1,28 +0,0 @@
package bot4cj.math
import std.random.*
import std.time.*
import std.math.*
/* -------- 神经网络使用的权重矩阵初始化类 --------
* TODO(20260517-qing): 将该函数改为枚举类,统一一个接口,根据模式匹配执行初始化
* - 这样在矩阵式中可以定义初始化方式 */
public class Ini {
/* xavier正态初始化 - 返回数组 */
public static func xavierNormalInit(fan_in: Int64, fan_out: Int64): Array<Array<Float64>> {
let seed = DateTime.now().nanosecond
let random = Random(UInt64(seed))
let sigma = sqrt(2.0f64 / Float64(fan_in + fan_out)) /* 标准差 */
var array = Array<Array<Float64>>(fan_in, repeat: Array<Float64>(fan_out, repeat: random.nextGaussianFloat64(mean: 0.0f64, sigma: sigma)))
return array
}
/* xavier正态初始化 - 返回数组 */
public static func xavierUniformInit(fan_in: Int64, fan_out: Int64): Array<Array<Float64>> {
let limit = sqrt(6.0f64 / Float64(fan_in + fan_out))
let seed = DateTime.now().nanosecond
let random = Random(UInt64(seed))
var array = Array<Array<Float64>>(fan_in, repeat: Array<Float64>(fan_out, repeat: (random.nextFloat64() * 2.0f64 - 1.0f64) * limit))
return array
}
}
-154
View File
@@ -1,154 +0,0 @@
package bot4cj.math
import std.time.*
import std.fs.*
import std.io.*
import std.random.*
import std.convert.*
import std.collection.*
import cangjie_tpc::matrix4cj.Matrix
public class Nns {
let nns = ArrayList<Nn>()
public func add(layer: Nn) {
nns.add(layer)
}
/* 【神经网络训练】
* description: 输入一组原始数据和教师数据,进行神经网络训练
* @param nns: 输入神经网络结构
* @param m: 输入一组原始数据
* @param t: 输入一组教师数据
* @param discount: 输入本次训练的折扣系数
*/
public func train(m: Matrix, t: Matrix, discount: Float64) {
/* ======== 这个对象用于暂时占位 ======== */
var res = Option<Matrix>.None
/* ======== 正向传播 ======== */
for (idx in 0..nns.size) {
let x = if (idx == 0) { m } else { res.getOrThrow() }
res = nns[idx].forward(x)
}
/* ======== 计算误差 ======== */
var errs = res.getOrThrow().minus(t)
/* ======== 反向传播 ======== */
for (idx in (nns.size-1)..=0:-1) {
errs = nns[idx].backward(errs)
}
/* ======== 梯度更新 ======== */
for (idx in 0..nns.size) {
nns[idx].update(discount)
}
/* ======== 计算MSE损失 ========
FIXME*(20260517-qing): 对于多个结果计算误差(目前只计算了第一个结果) */
let t0 = t.get(0, 0)
let r0 = res.getOrThrow().get(0, 0)
let loss0 = 0.5 * (t0 - r0) * (t0 - r0)
//let loss1 = 0.5 * (t1 - r1) * (t1 - r1)
//return (loss0 + loss1) / 2.0
return loss0
}
}
/*
* 20260823版本的神经网络,使用matrix4cj库
* TODO: 1.使用matrix4cj重写
* 2.使用数组表示批次
*/
public class Nn {
/* ---- 形状 ---- */
let inputno: Int64
let outputno:Int64
/* ---- 参数 ---- */
var weights: Matrix /* (inputs, outputs) */
var bias: Matrix /* (1, output) */
/* ---- 学习 ---- */
let lr: Float64
let f: Af /* 激活函数 */
/* ---- 缓存 ----*/
var prev_input: Option<Matrix> = None
var prev_raw_output: Option<Matrix> = None
var prev_output: Option<Matrix> = None
var prev_weight_grad: Option<Matrix> = None
/* ---- 梯度 ----*/
var weights_grad: Option<Matrix> = None
var bias_grad: Option<Matrix> = None
/* ---- 初始化器 ---- */
public init(i: Int64, o: Int64, lr: Float64, f: Af) {
inputno = i
outputno= o
this.lr = lr
this.f = f
weights = Matrix(Ini.xavierNormalInit(i, o))
bias = Matrix(1, o)
}
/* ---- 推理 ---- */
public func forward(x: Matrix) {
prev_input = x
var r = x.times(weights)
r = r.plus(bias)
prev_raw_output = r
var r1 = f.forward(r)
prev_output = r1
return r1
}
/* ---- 反向传播 ---- */
public func backward(e: Matrix) {
let fb = f.backward(prev_raw_output.getOrThrow())
let first = e.arrayTimes(fb)
let mx = prev_input.getOrThrow().transpose()
weights_grad = mx.times(first)
bias_grad = first
let e_up = first.times(weights.transpose())
return e_up
}
/* -------- 更新权重值、偏爱因子 --------
* NOTE: discount(折扣)是为了方便深度强化学习给出奖励值 */
public func update(discount: Float64) {
weights = weights.minus(weights_grad.getOrThrow() * lr * discount)
bias = bias.minus(bias_grad.getOrThrow() * lr * discount)
}
}
public func testNn() {
let m = Matrix(1, 2)
let t = Matrix(1, 2)
//let random = Random()
let random = Random(UInt64(DateTime.now().nanosecond))
let a = random.nextInt8(2)
let b = random.nextInt8(2)
let t0 = if ((a == 0 && b == 0) || (a ==1 && b == 1)) { 1.0f64 } else { 0.0f64 }
let t1 = if (a != b) { 1.0f64 } else { 0.0f64 }
m.set(0, 0, Float64(a))
m.set(0, 1, Float64(b))
t.set(0, 0, t0)
t.set(0, 1, t1)
let nns = Nns()
nns.add(Nn(2, 4, 0.01f64, Af.Leaky_ReLU(0.1f64)))
nns.add(Nn(4, 2, 0.01f64, Af.Sigmoid))
let num = 1000
let prt_num = 100
for (i in 0..num) {
let loss = nns.train(m, t, 1.0f64)
if (i % prt_num == prt_num-1) {
println("${i}\t: ${loss}")
}
}
}