移植bot4cj

This commit is contained in:
qing committed 2026-07-28 12:44:07 +08:00
1 parent 7803843155
commit 78ad3e6d57
76 files changed
+14468

No files matched your search

+263
View File
@@ -0,0 +1,263 @@
package bot4cj
import std.io.*
import std.math.*
import std.convert.*
import std.collection.*
import bot4cj.math.*
/* -------- 矩阵编程语言 - 基本对象 --------
* TODO:增加储存到文件系统的功能
* TODO:神经网络层转换为矩阵对象
-------- */
class MatrixObject {
private var name: String = "" /* -------- 名字 -------- */
private var tmp = ArrayList<Rune>() /* ---- rune buf 字符缓冲区 ---- */
private var grp = ArrayList<String>() /* ---- one line 整行缓冲区 ---- */
private var values = ArrayList<ArrayList<String>>() /* ---- 原始矩阵 ---- */
/* -------- 是否已命名 -------- */
public func hasName(): Bool { !(name == "") }
/* --------- 获得名字 --------- */
public func getName(): String { return name }
/* ------- 取动作的长度 ------- */
public mut prop size: Int64 {
get() { values.size }
set(_) {}
}
/* -------- 取动作的列数 -------- */
public mut prop col: Int64 {
get() {
if (this.size > 0) {
return values[0].size }
else { return 0 }
}
set(_) {}
}
/* ------- 获取矩阵元素,只能取,不能修改 ------- */
public operator func [](row: Int64, col: Int64): String {
return values[row][col]
}
/** -- 在某一行中寻找某一个字符串的列号 -- */
public func indexOfCol(row: Int64, key: String): Int64 {
for (col in 0..values[row].size) {
if (values[row][col] == key) {
return col }
}
/* ---- 没找到 ---- */
throw IllegalArgumentException("The key does not found.")
}
/* ----- 储存对象 ----- */
public func save(output: StringWriter<OutputStream>) {
output.writeln("@${getName()}")
for (vec in values) {
for (item in vec) {
output.write(item)
output.write(" ")
}
output.writeln(";")
}
output.writeln("@")
}
/* -------- 添加字符 -------- */
public func addRune(c: Rune) { tmp.add(c) }
/* -------- 提交名字 -------- */
public func submitName() {
if (tmp.size == 0) { return }
let s = String(tmp)
tmp.clear()
name = s
}
/* -------- 提交元素 -------- */
public func submitItem() {
if (tmp.size == 0) { return }
let s = String(tmp)
tmp = ArrayList<Rune>()
grp.add(s)
}
/* -------- 提交行 -------- */
public func submitLine(count: Int64) {
if (grp.size == 0) { return }
if (!values.size == 0 && !grp.size == values[values.size - 1].size)
{ throw DoesNotSync(count) }
values.add(grp)
grp = ArrayList<String>()
}
}
/* ---------------- 对Nn1进行扩展 ---------------- */
extend Nn1 {
/* -------- 转换为MatrixObject对象 -------- */
public func toMto(): MatrixObject {
let mto = MatrixObject()
return mto
}
}
/* -------- 矩阵式解析器 --------
* TODO(20260514-qing): 这个类输入流数据,输出MatrixObject对象
* 它是与其深度绑定的,我倾向于将其与MatrixObject融合
* 即持有HashMap<String, ArrayList<ArrayList<String>>> */
class MatrixParser {
/* ---- 静态判断Option<Rune>是否有值 ----
- 该方法用于文本文件遍历时判空 */
private static func hasValue(co: Option<Rune>): Bool {
match(co) {
case Some(_) => true
case None => false
}
}
/* -------- 主初始化器(构造函数) --------
- 设置目标输入流 */
private var input: StringReader<InputStream>
public init(input: StringReader<InputStream>) {
this.input = input }
/* -------- 切换目标输入流 -------- */
public func reset(input: StringReader<InputStream>) {
this.input = input }
/* -------- 解析器 - 循环不变式 ----------------------
* -------- 初始化: ss是一个动作脚本的输入流 --------
* -------- 保持: ss是动作脚本剩余部分的输入流 ------
* -------- 终止: 当输入流为空时,循环终止 ---------- */
private var obj = MatrixObject()
public func parse(): Option<MatrixObject> {
var count = 1i64 /* 行计数器始于1 */
flag = false /* 重置结束标志 */
this.obj = MatrixObject() /* 创建一个新的对象,一次只解析一个对象 */
var so = input.read() /* 获取初始状态 */
while (!flag && hasValue(so)) {
try {
let s = so.getOrThrow()
print(s) /* debug */
count = _next(s, count) /* 进行解析解析 */
so = input.read() /* 为下一次循环提供条件 */
}
catch (exp: BufOverTheLine) { throw exp }
catch (exp: IllegalArgumentException) { throw IllegalArgumentException("Parse failed at line ${count}") }
catch (exp: Exception) { throw UnknowExcept(count) } /* FIXME: 还是暴露出原来的错误位置来更好? */
}
/* 如果没有新的矩阵式对象(意外终止)则返回空 */
if (!obj.hasName()) { return None }
return obj /* 否则为解析成功正常退出,返回解析到的矩阵式对象 */
}
private var flag = false /* 结束标志 */
private var nl: Bool = true /* new line */
private var cm: Bool = false /* comment */
private var nm: Bool = false /* name-line */
private func _next(c: Rune, ct: Int64): Int64 {
var count = ct
/* ------------ COMMENTS ------------ */
if (!cm && c == r'#') { cm = true } /* 注释开始时 */
else if (cm && c == r'\n') { /* 注释结束时 */
nl = true
cm = false
count += 1
}
else if (cm && c != r'\n') { ; } /* 注释进行时 */
/* FIXME: EOF情况 */
/* ------------ new line ------------ */
else if (nl && (c == r' ' || c == r'\t' || c == r'\r')) { ; } /* 新行空字符,维持现状 */
else if (nl && c == r'\n') { /* 新行空行 */
count += 1
}
else if (nl && c == r'@' && !obj.hasName()) {
nl = false
nm = true
}
else if (nl && c == r'@' && obj.hasName()) {
flag =true
nl = false
nm = true
}
else if (nl && c != r'@') { /* 新行内容 */
nl = false
obj.addRune(c)
}
/* ------------- not nl ------------- */
else if (!nl && nm && (c == r' ' || c == r'\t' || c == r'\n')) { /* 添加名字 */
obj.submitName()
nl = true
nm = false
}
else if (!nl && !nm && (c == r' ' || c == r'\t' || c == r"\n")) { /* 添加命名项 */
obj.submitItem()
if (c == r"\n") { count += 1 }
}
else if (!nl && c == r';') { /* 普通行结束 */
obj.submitItem()
obj.submitLine(count)
nl = true
}
else if (!nl) { // 非空格、非制表符、非分号
obj.addRune(c)
}
/* -------- 返回行号 -------- */
return count
}
}
/* -------- 矩阵式储存器 --------
* - 接收矩阵式的哈希表(HashMap<String, MatrixObject>)
* - 以及一个输出流(在构造函数)。 */
class MatrixSaver {
private let stringWriter: StringWriter<OutputStream>
public init(output: OutputStream) {
stringWriter = StringWriter(output) }
public func save(C: LinkedList<MatrixObject>) {
for (item in C) {
item.save(stringWriter)
stringWriter.writeln() /* 对象之间空一行 */
stringWriter.flush() }
}
}
/* --------------------------------------------------------- */
/* --------------------------------------------------------- */
/* TODO(20260516-qing): 继续完善异常种类 */
/* -------- 解析异常 -------- */
open class ParseExcept <: Exception {
public ParseExcept(msg: String, line: Int64) {
super('${msg}. we got line: ${line}') }
}
/* -------- 缓冲区过长 -------- */
class BufOverTheLine <: ParseExcept {
BufOverTheLine(count: Int64) {
super('Buffer length over the line', count) }
}
/* -------- 行没对齐 -------- */
class DoesNotSync <: ParseExcept {
DoesNotSync(count: Int64) {
super("Matrix Line Does Not Sync", count) }
}
/* -------- 未知异常 -------- */
class UnknowExcept <: ParseExcept {
UnknowExcept(count: Int64) {
super("I don't understand what happened", count) }
}
+152
View File
@@ -0,0 +1,152 @@
/*
* i2c - 用户界面
*
* 这里已经提供了I2C通用接口,可以将上层代码移动到仓颉下面
*/
#include <stdio.h>
#include <stdint.h>
#include <fcntl.h>
#ifdef __linux__
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h> /* 其中宏的取值可能具有平台特异性 */
#include <i2c/smbus.h>
#endif
#include "bot4cj.h" /* 有没有可能在仓颉代码中解析C头文件取得宏 */
/*
* 初始化i2c设备
*
* 动态指定设备文件
*
* 返回打开的文件句柄或者错误码
*/
#ifdef __linux__
int16_t I2C_init(const char* filepath) {
int16_t fd;
if ((fd = open(filepath, O_RDWR)) < 0) {
perror("Failed to open I2C bus");
return BOT4CJ_I2C_OPENED_FAILED;
}
else {
return fd;
}
}
#endif
#ifdef _WIN64
int16_t I2C_init(const char* filepath) {
return BOT4CJ_I2C_OPENED_FAILED;
}
#endif
/* 释放I2C设备文件 */
#ifdef __linux__
void I2C_free(int16_t fd) {
close(fd);
}
#endif
#ifdef _WIN64
void I2C_free(int16_t fd) {
/* Windows平台下无需操作 */
}
#endif
/* 设置I2C地址 */
#ifdef __linux__
int I2C_setAddr(int16_t fd, uint8_t addr) {
if (ioctl(fd, I2C_SLAVE, addr) < 0) {
perror("Failed to set I2C address");
return BOT4CJ_I2C_ERR;
}
else {
return BOT4CJ_I2C_OK;
}
}
#endif
#ifdef _WIN64
int I2C_setAddr(int16_t fd, uint8_t addr) {
return BOT4CJ_I2C_ERR;
}
#endif
/* 写寄存器 */
#ifdef __linux__
int I2C_writeReg(int16_t fd, uint8_t reg, uint8_t val) {
/*uint8_t buf[] = {reg, val};
if (write(fd, buf, 2) != 2) {
perror("I2C write error");
return BOT4CJ_I2C_ERR;
}
else {
return BOT4CJ_I2C_OK;
}*/
return i2c_smbus_write_byte_data(fd, reg, val);
}
#endif
#ifdef _WIN64
int I2C_writeReg(int16_t fd, uint8_t reg, uint8_t val) {
return BOT4CJ_I2C_ERR;
}
#endif
/* 读寄存器 失败返回0 */
#ifdef __linux__
uint8_t I2C_readReg(int16_t fd, uint8_t reg) {
uint8_t value;
/*if (write(fd, &reg, 1) != 1) {
perror("I2C read error");
return 0;
}
if (read(fd, &value, 1)!= 1) {
perror("I2C read error");
return 0;
}*/
//return value;
int8_t a = i2c_smbus_read_byte_data(fd, reg);
return *(uint8_t *)& a;
}
#endif
#ifdef _WIN64
uint8_t I2C_readReg(int16_t fd, uint8_t reg) {
return 0;
}
#endif
/* 读寄存器 - 有符号
* 失败返回0 */
#ifdef __linux__
int8_t I2C_readRegFlag(int16_t fd, uint8_t reg) {
int8_t value;
//if (write(fd, &reg, 1) != 1) {
//perror("I2C read error");
//return 0;
//}
//if (read(fd, &value, 1)!= 1) {
//perror("I2C read error");
//return 0;
//}
//return value;
return i2c_smbus_read_byte_data(fd, reg);
}
#endif
#ifdef _WIN64
int8_t I2C_readRegFlag(int16_t fd, uint8_t reg) {
return 0;
}
#endif
+516
View File
@@ -0,0 +1,516 @@
#include <random>
#include <algorithm>
#include "Matrix.hh"
namespace qing {
template <typename T>
Matrx<T>::Matrx(): row(0), col(0) {}
template <typename T>
Matrx<T>::Matrx(const uint64_t row, const uint64_t col)
: row(row), col(col) { /* 该构造函数用于从头开始构造 */
arr = std::vector<T>(col * row);
}
template<typename T>
Matrx<T>::Matrx(const uint64_t row, const uint64_t col, const std::vector<T>& src)
: row(row), col(col) { /* 该构造函数用于通过已有的vector进行构造 */
arr = src;
}
template<typename T>
const T& Matrx<T>::get(uint64_t row, uint64_t col) const { /* 访问器 - 访问矩阵元素 */
return arr[col * this->row + row];
}
template<typename T>
T& Matrx<T>::witch(uint64_t row, uint64_t col) { /* 访问器 - 访问矩阵元素 */
return arr[col * this->row + row];
}
template<typename T>
void Matrx<T>::set(uint64_t row, uint64_t col, T v) { /* 设置矩阵元素 */
arr[col * this->row + row] = v;
}
template<typename T>
const std::vector<T>& Matrx<T>::get_data() const{ /* 访问器 - 用于获取原始数据 */
return arr;
}
template<typename T>
const uint64_t Matrx<T>::get_col() const { /* 获取列数 */
return col;
}
template<typename T>
const uint64_t Matrx<T>::get_row() const { /* 获取行数 */
return row;
}
template<typename T>
bool Matrx<T>::has_same_shape(const Matrx<T>& other) { /* 两个矩阵是否具有相同形状 */
return other.get_col() == this->get_col() && other.get_row() == this->get_row();
}
template<typename T>
bool Matrx<T>::has_dot_shape(const Matrx<T>& other) { /* 具有矩阵乘法的形状 */
return this->get_col() == other.get_row();
}
template<typename T>
void Matrx<T>::assert_same_shape(const Matrx<T>& other) { /* 断言两个矩阵具有相同形状 */
if (!has_same_shape(other))
throw std::invalid_argument("The shapes of matrix are not same.");
}
template<typename T>
void Matrx<T>::assert_dot_shape(const Matrx<T>& other) { /* 断言两个矩阵具有点积前置形状 */
if (!has_dot_shape(other)) {
throw std::invalid_argument("The shapes of matrix can not be dot.");
}
}
template<typename T>
Matrx<T> Matrx<T>::operator+(const Matrx<T>& other) { /* 加法操作符重定义 - 矩阵加法 */
//assert_same_shape(other); /* 检查两个矩阵形状是否一致 */
std::vector<T> v0 = this->get_data();
std::vector<T> vec = other.get_data();
for (auto it = v0.begin(), it_or = vec.begin(); it != v0.end(); ++it, ++it_or )
*it += *it_or;
return Matrx<T>{this->get_row(), this->get_col(), v0};
}
template<typename T>
Matrx<T> Matrx<T>::operator+(T& item) { /* 矩阵加法广播 */
auto len = this->get_data().size();
auto v0 = std::vector<T> (len, item);
auto matrix = (*this) + Matrx(get_row(), get_col(), v0);
//auto matrix = *this;
//for (auto it = arr.begin(); it != arr.end(); ++it)
// *it += item;
return matrix;
}
template<typename T>
Matrx<T> Matrx<T>::operator-(const Matrx<T>& other) { /* 操作符重载 - 矩阵减法 */
//assert_same_shape(other); /* 检查两个矩阵形状是否一致 */
std::vector<T> v0 = this->get_data();
std::vector <T> vec = other.get_data();
for (auto it = v0.begin(), it_or = vec.begin(); it != v0.end(); ++it, ++it_or )
*it -= *it_or;
return Matrx{this->get_row(), this->get_col(), v0};
}
template<typename T>
Matrx<T> Matrx<T>::operator-(T& item) { /* 操作符重载 - 矩阵减法广播 */
auto len = this->get_data().size();
auto v0 = std::vector<T> (len, item);
auto matrix = (*this) - Matrx(get_row(), get_col(), v0);
//Matrx<T> matrix = *this;
//for (auto it = arr.begin(); it != arr.end(); ++it)
// *it -= item;
return matrix;
}
template<typename T>
Matrx<T> Matrx<T>::dot(const Matrx<T>& other) { /* 操作符重载 - 矩阵乘法 */
//assert_dot_shape(other); /* 它们的形状是否能够点积 */
auto matrix = Matrx<T>(this->get_row(), other.get_col());
for (uint64_t i=0; i<this->get_row(); ++i) /* 点积运算 */
for (uint64_t j=0; j<other.get_col(); ++j) {
T sum{0};
for (uint64_t k=0; k<this->get_col(); ++k)
sum += this->get(i, k) * other.get(k, j);
matrix.witch(i, j) = sum;
}
return matrix;
}
template<typename T>
Matrx<T> Matrx<T>::operator*(T& item) { /* 操作符重载 - 矩阵乘法广播 */
std::vector<T> vec = this->get_data();
for (auto it = vec.begin(); it != vec.end(); ++it)
*it *= item;
return Matrx<T>{this->get_row(), this->get_col(), vec};
}
template<typename T>
Matrx<T> Matrx<T>::operator*(const Matrx<T>& other) { /* 加法操作符重定义 - 矩阵加法 */
//assert_same_shape(other); /* 检查两个矩阵形状是否一致 */
std::vector<T> v0 = this->get_data();
std::vector<T> vec = other.get_data();
for (auto it = v0.begin(), it_or = vec.begin(); it != v0.end(); ++it, ++it_or )
*it *= *it_or;
return Matrx<T>{this->get_row(), this->get_col(), v0};
}
template<typename T>
Matrx<T> Matrx<T>::operator/(T& item) { /* 操作符重载 - 矩阵除法广播 */
std::vector<T> vec = this->get_data();
for (auto it = vec.begin(); it != vec.end(); ++it)
*it /= item;
return Matrx<T>(this->get_row(), this->get_col(), vec);
}
template<typename T>
Matrx<T> Matrx<T>::operator/(const Matrx<T>& other) { /* 加法操作符重定义 - 矩阵加法 */
//assert_same_shape(other); /* 检查两个矩阵形状是否一致 */
std::vector<T> v0 = this->get_data();
std::vector<T> vec = other.get_data();
for (auto it = v0.begin(), it_or = vec.begin(); it != v0.end(); ++it, ++it_or )
*it /= *it_or;
return Matrx<T>{this->get_row(), this->get_col(), v0};
}
template class Matrx<float>; /* 显式实例化 */
template class Matrx<double>;
}
/* FIXME: 形状不合法时返回空指针 */
using namespace qing;
extern "C" {
#ifdef _WIN64
_declspec(dllexport)
#endif
void* createMatrix(uint64_t row, uint64_t col) {
return (void *) new Matrx<float>(row, col);
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void freeMatrix(void* M) {
delete (Matrx<float>*) M;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
float* witchMatrix(void *m, uint64_t row, uint64_t col) {
Matrx<float> *M = (Matrx<float>*) m;
return &(M->witch(row, col));
}
#ifdef _WIN64
_declspec(dllexport)
#endif
float getMatrix(void *m, uint64_t row, uint64_t col) {
Matrx<float> *M = (Matrx<float>*) m;
return M->get(row, col);
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void setMatrix(void *m, uint64_t row, uint64_t col, float v) {
Matrx<float> *M = (Matrx<float>*) m;
M->set(row, col, v);
}
#ifdef _WIN64
_declspec(dllexport)
#endif
uint64_t getrowMatrix(void *m) {
Matrx<float> *M = (Matrx<float>*) m;
return M->get_row();
}
#ifdef _WIN64
_declspec(dllexport)
#endif
uint64_t getcolMatrix(void *m) {
Matrx<float> *M = (Matrx<float>*) m;
return M->get_col();
}
#ifdef _WIN64
_declspec(dllexport)
#endif
bool sameShapeMatrix(void *m, void *s) {
Matrx<float> *M = (Matrx<float>*) m;
Matrx<float> *S = (Matrx<float>*) s;
return M->has_same_shape(*S);
}
#ifdef _WIN64
_declspec(dllexport)
#endif
bool dotShapeMatrix(void *m, void *s) {
Matrx<float> *M = (Matrx<float>*) m;
Matrx<float> *S = (Matrx<float>*) s;
return M->has_dot_shape(*S);
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* plusMatrix(void *m, void *s) {
Matrx<float> *M = (Matrx<float>*) m;
Matrx<float> *S = (Matrx<float>*) s;
auto a = new Matrx<float>((*M) + (*S));
return (void *) a;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* plusBroadcastMatrix(void *m, float t) {
Matrx<float> *M = (Matrx<float>*) m;
auto a = new Matrx<float> ((*M) + t);
return (void*) a;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* subMatrix(void *m, void *s) {
Matrx<float> *M = (Matrx<float>*) m;
Matrx<float> *S = (Matrx<float>*) s;
auto a = new Matrx<float>((*M) - (*S));
return (void *) a;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void * subBroadcastMatrix(void *m, float t) {
Matrx<float> *M = (Matrx<float> *) m;
auto a = new Matrx<float> ((*M) - t);
return (void *) a;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* multiMatrix(void *m, void *s) {
Matrx<float> *M = (Matrx<float>*) m;
Matrx<float> *S = (Matrx<float>*) s;
auto a = new Matrx<float>((*M) * (*S));
return (void *) a;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* multiBroadcastMatrix(void *m, float t) {
Matrx<float> *M = (Matrx<float> *) m;
auto a = new Matrx<float>((*M) * t);
return (void*) a;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* divMatrix(void *m, void *s) {
Matrx<float> *M = (Matrx<float>*) m;
Matrx<float> *S = (Matrx<float>*) s;
auto a = new Matrx<float>((*M) / (*S));
return (void *) a;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* divBroadcastMatrix(void *m, float t) {
Matrx<float> *M = (Matrx<float> *) m;
auto a = new Matrx<float>((*M) / t);
return (void*) a;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* dotMatrix(void *m, void *s) {
Matrx<float> *M = (Matrx<float>*) m;
Matrx<float> *S = (Matrx<float>*) s;
auto a = new Matrx<float>(M->dot(*S));
return (void *) a;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* transposeMatrix(void *m) {
Matrx<float> *M = (Matrx<float> *)m;
auto N= new Matrx<float>(M->get_col(), M->get_row());
for (uint64_t row = 0; row< N->get_row(); row++) {
for (uint64_t col = 0; col < N->get_col(); col++) {
N->witch(row, col) = M->get(col, row);
}
}
return (void *)N;
}
/* 正态分布Xavier初始化 */
#ifdef _WIN64
_declspec(dllexport)
#endif
void* xavierNormalInit(uint64_t fan_in, uint64_t fan_out)
{
/* 构建空数组 */
std::vector<float> weights(fan_in * fan_out);
/* 高质量的随机数生成器 */
static std::mt19937 generator(std::random_device{}());
/* 计算标准差 */
float sigma = std::sqrt(2.0f / (fan_in + fan_out));
/* 创建正态分布对象,均值为0,标准差为sigma */
std::normal_distribution<float> distribution(0.0f, sigma);
/* 遍历权重矩阵,进行赋值 */
uint64_t k=0;
for (uint64_t i=0; i<fan_in; ++i)
for (uint64_t j=0; j<fan_out; ++j)
weights[k++] = distribution(generator);
/* 构建矩阵 */
auto matrix = new Matrx<float>(fan_in, fan_out, weights);
return (void *)matrix;
}
/* 均匀分布Xavier初始化函数 */
#ifdef _WIN64
_declspec(dllexport)
#endif
void* xavierUniformInit(uint64_t fan_in, uint64_t fan_out)
{
/* 构建空的数组 */
auto weights = std::vector<float>(fan_in * fan_out);
/* 使用高质量的随机数生成器 */
static std::mt19937 generator(std::random_device{}());
/* 计算区间范围:sqrt(2 / (fan_in + fan_out)) */
float scale = std::sqrt(2.0f / (fan_in + fan_out));
/* 创建均匀分布对象,范围为[-scale, scale] */
std::uniform_real_distribution<float> distribution(-scale, scale);
/* 遍历权重矩阵,进行赋值 */
uint64_t k=0;
for (uint64_t i=0; i<fan_in; ++i)
for (uint64_t j=0; j<fan_out; ++j)
weights[k++] = distribution(generator);
/* 构建矩阵对象 */
auto matrix = new Matrx<float> (fan_in, fan_out, weights);
return (void *)matrix;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* Sigmoid_do(void *m) {
Matrx<float> *M = (Matrx<float>*) m;
std::vector<float> v = M->get_data();
for (float& x: v) {
x = 1.0 / (1.0 + std::exp(-x));
}
auto N = new Matrx<float> (M->get_row(), M->get_col(), v);
return (void *)N;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* Sigmoid_dot(void *m) {
Matrx<float> *M = (Matrx<float>*) m;
std::vector<float> v = M->get_data();
for (float& o: v) {
o = o * (1.0 - o);
}
auto N = new Matrx<float> (M->get_row(), M->get_col(), v);
return (void *)N;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* ReLU_do(void *m) {
Matrx<float> *M = (Matrx<float>*) m;
std::vector<float> v = M->get_data();
for (float& x: v) {
x = x > 0 ? x : 0;
}
auto N = new Matrx<float> (M->get_row(), M->get_col(), v);
return (void *)N;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* ReLU_dot(void *m) {
Matrx<float> *M = (Matrx<float>*) m;
std::vector<float> v = M->get_data();
for (float& x: v) {
x = x > 0 ? 1.0f : 0.0f;
}
auto N = new Matrx<float> (M->get_row(), M->get_col(), v);
return (void *)N;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* Leaky_ReLU_do(void *m, float alpha) {
Matrx<float> *M = (Matrx<float>*) m;
std::vector<float> v = M->get_data();
for (float& x: v) {
x = x > 0 ? x : alpha * x;
}
auto N = new Matrx<float> (M->get_row(), M->get_col(), v);
return (void *)N;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* Leaky_ReLU_dot(void *m, float alpha) {
Matrx<float> *M = (Matrx<float>*) m;
std::vector<float> v = M->get_data();
for (float& x: v) {
x = x > 0 ? 1.0 : alpha;
}
auto N = new Matrx<float> (M->get_row(), M->get_col(), v);
return (void *)N;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* Tanh_do(void *m) {
Matrx<float> *M = (Matrx<float>*) m;
std::vector<float> v = M->get_data();
for (float& x: v) {
x = std::tanh(x);
}
auto N = new Matrx<float> (M->get_row(), M->get_col(), v);
return (void *)N;
}
#ifdef _WIN64
_declspec(dllexport)
#endif
void* Tanh_dot(void *m) {
Matrx<float> *M = (Matrx<float>*) m;
std::vector<float> v = M->get_data();
for (float& x: v) {
if (x > 20.0) x = 0.0; // tanh(x) ≈ 1, 导数≈0
else if (x < -20.0) x = 0.0; // tanh(x) ≈ -1, 导数≈0
else {
float t = tanh(x);
x = 1.0 - t * t;
}
}
auto N = new Matrx<float> (M->get_row(), M->get_col(), v);
return (void *)N;
}
}
+60
View File
@@ -0,0 +1,60 @@
/*
___col___
/ \
+---------+ \
| + + + + | | row - 第一维度
| + + + + | |
+---------+ /
*/
#pragma once
#include <cmath>
#include <vector>
#include <iostream>
#include <stdexcept>
#include <cstdint>
namespace qing {
template <class T>
class Matrx {
public:
Matrx();
Matrx(const uint64_t row, const uint64_t col);
Matrx(const uint64_t row, const uint64_t col, const std::vector<T>& src);
const T& get(uint64_t row, uint64_t col) const;
//-------------旧接口----------------
T& witch(uint64_t row, uint64_t col);
//-------------新接口----------------
void set(uint64_t row, uint64_t col, T v);
//-----------------------------------
const std::vector<T>& get_data() const;
const uint64_t get_col() const;
const uint64_t get_row() const;
bool has_same_shape(const Matrx<T>& other);
bool has_dot_shape(const Matrx<T>& other);
void assert_same_shape(const Matrx<T>& other);
void assert_dot_shape(const Matrx<T>& other);
Matrx<T> operator+(const Matrx<T>& other);
Matrx<T> operator+(T& item);
Matrx<T> operator-(const Matrx<T>& other);
Matrx<T> operator-(T& item);
Matrx<T> operator*(const Matrx<T>& other);
Matrx<T> operator*(T& item);
Matrx<T> operator/(T& item);
Matrx<T> operator/(const Matrx<T>& other);
Matrx<T> dot(const Matrx<T>& other);
void save(std::ostream& out) {
out << row << " " << col;
for (const auto& item : arr) {
out << " ";
out << item;
}
}
private:
uint64_t row, col;
std::vector<T> arr;
};
}
+21
View File
@@ -0,0 +1,21 @@
/* I2C操作库 - PCA9685 - MPU6050 */
#ifndef QING_I2C
#define QING_I2C
/* I2C */
#define BOT4CJ_I2C_OK 0
#define BOT4CJ_I2C_ERR 1
#define BOT4CJ_I2C_OPENED_FAILED -1
#ifdef _WIN64
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
DLLEXPORT int16_t I2C_init(const char* filepath);
DLLEXPORT void I2C_free(int16_t fd);
DLLEXPORT int I2C_setAddr(int16_t fd, uint8_t addr);
DLLEXPORT int I2C_writeReg(int16_t fd, uint8_t reg, uint8_t val);
DLLEXPORT uint8_t I2C_readReg(int16_t fd, uint8_t reg);
DLLEXPORT int8_t I2C_readRegFlag(int16_t fd, uint8_t reg);
#endif
+80
View File
@@ -0,0 +1,80 @@
#define _USE_MATH_DEFINES
#include <cmath>
#include <vector>
#include "fft.h"
#include "Matrix.hh"
using namespace qing;
void functf1d(float A[DN])
{
unsigned long int i;
float dt, tt;
float p = 0.1;
float freq = 10.0; // 10Hz
float fs = DN; // 采样率65536Hz
dt = 1.0 / fs;
for(i = 0; i < DN; i++)
{
tt = (float)i * dt;
A[i] = sin(2.0 * M_PI * freq * tt) * p; // 正弦波
//A[1][i] = 0.0; // 虚部为0
}
}
// 初始化FFT数组
static void fft_init(float A[2][DN], Matrx<float>& M)
{
for (int i = 0; i < DN; i++) {
float a = M.witch(i, 0);
A[0][i] = a;
A[1][i] = 0;
}
}
// 计算FFT结果
static void fft_result(std::vector<float>& R, float A[2][DN])
{
for (int i = 0; i < DN/2; i++) {
float a = pow(A[0][i], 2) + pow(A[1][i], 2);
R[i] = sqrt(a) / (DN/2);
//std::cout << i << ":\t" << R[i] << std::endl;
}
}
extern "C" {
// 生成正弦波
#ifdef _WIN64
__declspec(dllexport) void* gen_wave()
#else
void* gen_wave()
#endif
{
std::vector<float> A(DN);
functf1d(A.data());
return (void *)new Matrx<float>(DN, 1, A);
}
// 开始FFT
#ifdef _WIN64
__declspec(dllexport) void* start_fft(void* M)
#else
void* start_fft(void* M)
#endif
{
float A[2][DN];
std::vector<float> R(N);
fft_init(A, *(Matrx<float> *)M);
//fft1(A, 1); // 复序列基2 FFT
fft1r1(A, 1); // 单实序列
fft_result(R, A);
return (void*) new Matrx<float>(N/2, 1, R);
}
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef FFT_H
#define FFT_H
#ifndef N
#define N 32768
#endif
#ifndef DN
#define DN (N*2)
#endif
#define eps 1.0e-9
#ifdef __cplusplus
extern "C" {
#endif
void initial(void);
void fft1(float A[2][N], int ifft);
unsigned long int inverseq(unsigned long int i1,
unsigned long int imax);
void fft1r2(float A[2][N], float B[2][N], int ifft);
void fft1r1(float A[2][DN], int ifft);
#ifdef __cplusplus
}
#endif
#endif
+231
View File
@@ -0,0 +1,231 @@
/* fft1.c: fast Fourier transform base on 2 for one dimensional
complex array, direct and inverse,
This program is contributed by Jiang Yong, 2003. 1. */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "fft.h"
static int ifirst=0, sign=-1;
static unsigned M, jup[64], kup[64];
static float pi, wr[N], wi[N];
/********* initial **************/
void initial(void)
{
unsigned long int i, j, ni;
float unit, tt;
ni = N;
M = 0;
while (ni > 1) { ni=ni/2; M=M+1; }
ni = 1;
for(j=0; j<M; j++) ni = ni * 2;
if ((ni-N) != 0)
{
printf("N isn't compatible! (N => %i, ni -> %lu)\n", N, ni);
exit(1);
}
kup[0] = N/2; /* kup[l] = 2 ^ (M - l - 1) */
jup[0] =1;
for (j=1;j<M;j++)
{
kup[j]=kup[j-1]/2;
jup[j]=jup[j-1]*2;
}
pi=4.0*atan((float)1.0);
unit=2.0*pi/((float)N);
tt=0.0;
for(i=0;i<N;i++)
{
wr[i] = cos(tt);
wi[i] = sign * sin(tt);
tt = tt + unit;
}
}
/** ifft=1<-->FFT ** ifft=-1<-->IFFT **/
void fft1(float A[2][N], int ifft)
{
unsigned long int l, k, i, j, jl0, jl0k, jl1k, reseq, ij;
float wrc, wis, ar, ai, x[2][N];
if (ifirst == 0) { initial(), ifirst=1; }
if (ifft == -1)
for(i=0;i<N;i++) A[1][i] = -A[1][i];
for (l=0; l<M; l++)
{
for (j=0; j<jup[l]; j++)
{
reseq=inverseq(j, l);
ij=kup[l]*reseq;
jl0=2*kup[l]*j;
wrc = wr[ij];
wis = wi[ij];
for (k=0; k<kup[l]; k++)
{
jl0k = jl0 + k;
jl1k = jl0 + kup[l] + k;
ar = A[0][jl1k] * wrc - A[1][jl1k] * wis;
ai = A[0][jl1k] * wis + A[1][jl1k] * wrc;
A[0][jl1k] = A[0][jl0k] - ar;
A[1][jl1k] = A[1][jl0k] - ai;
A[0][jl0k] = A[0][jl0k] + ar;
A[1][jl0k] = A[1][jl0k] + ai;
}
}
}
for (i=0; i<N; i++)
{
j=inverseq(i, M);
x[0][i] = A[0][j];
x[1][i] = A[1][j];
}
for (i=0; i<N; i++)
{
A[0][i] = x[0][i];
A[1][i] = x[1][i];
}
if (ifft == -1)
for (i=0; i<N; i++)
{
A[0][i] = A[0][i]/((float)N);
A[1][i] = -A[1][i]/((float)N);
}
}
/*********************************/
unsigned long int inverseq(unsigned long int i1,
unsigned long int imax)
{
unsigned long int j, ii, i0, inv;
i0 = i1;
inv = 0;
for (j=0; j < imax; j++)
{
ii=i0%2;
inv=2*inv+ii;
i0=(i0-ii)/2;
}
return (inv);
}
/* ifft=1 <--> FFT ** ifft=-1 <--> IFFT */
void fft1r2(float A[2][N], float B[2][N], int ifft)
{
unsigned long int k, j;
float norm, c[2][N];
if (ifft != -1)
{
for (k=0; k<N; k++)
{
c[0][k]=A[0][k];
c[1][k]=B[0][k];
}
fft1(c, 1);
A[0][0] = c[0][0];
A[1][0] = 0.0;
B[0][0] = c[1][0];
B[1][0] = 0.0;
for (j=1; j<N; j++)
{
A[0][j] = 0.5 * (c[0][j] + c[0][N-j]);
A[1][j] = 0.5 * (c[1][j] - c[1][N-j]);
B[0][j] = 0.5 * (c[1][j] + c[1][N-j]);
B[1][j] = -0.5 * (c[0][j] - c[0][N-j]);
}
} /* end if*/
else
{
for (j=0; j<N/2; j++)
/* check {xj}, {yj} is to real {Ak}, {Bk} */
{
if (j == 0) norm = 2.0 * fabs(A[1][0])
+ 2.0 * fabs(B[1][0]);
else norm = fabs(A[1][j]+A[1][N-j])
+ fabs(A[0][j]-A[0][N-j])
+ fabs(B[1][j]+B[1][N-j])
+ fabs(B[0][j]-B[0][N-j]);
if (norm > eps)
{
printf("The IDFT of the series isn't real!\n");
exit(0);
}
}
for (j=0; j<N; j++)
{
c[0][j]=A[0][j] -B[1][j];
c[1][j]=A[1][j] +B[0][j];
}
fft1(c, -1);
for (k=0; k<N; k++)
{
A[0][k] = c[0][k];
B[0][k] = c[1][k];
A[1][k] = 0.0;
B[1][k] = 0.0;
}
} /* end else */
}
/* ifft = 1 <--> FFT ** ifft = -1 <--> IFFT */
void fft1r1(float A[2][DN], int ifft)
{
unsigned long int j, k, dk;
float norm, c[2][N], d[2][N], tt, hunit, cosj, sinj;
hunit = 4.0 * atan(1.0) / (float)N;
if ( ifft != -1)
{
for (k=0; k<N; k++)
{
dk = k + k;
c[0][k] = A[0][dk];
d[0][k] = A[0][dk+1];
c[1][k] = 0.0;
d[1][k] = 0.0;
}
fft1r2(c, d, 1);
for (j = 0; j < N; j++)
{
tt = (float)j * hunit;
cosj = cos(tt);
sinj = sign * sin(tt);
A[0][j] = c[0][j] + cosj * d[0][j] - sinj * d[1][j];
A[1][j] = c[1][j] + cosj * d[1][j] + sinj * d[0][j];
}
A[0][N] = c[0][0] - d[0][0];
A[1][N] = c[1][0] - d[1][0];
for(j=1; j<N; j++)
{
A[0][DN-j] = A[0][j];
A[1][DN-j] = -A[1][j];
}
} // end if
else
{
for (j = 0; j <N; j++) // check {xj} is to real {Ak}
{
if (j ==0) norm = 2.0 * fabs(A[1][0]);
else norm = fabs(A[1][j]+A[1][DN-j])+
fabs(A[0][j]-A[0][DN-j]);
if (norm > eps)
{
printf("The IDFT of the series {xj} isn't real!\n");
exit(0);
}
}
for (j = 0; j <N; j++)
{
c[0][j]= 0.5 * (A[0][j]+A[0][N+j]);
c[1][j]= 0.5 * (A[1][j]+A[1][N+j]);
tt = (float) j * hunit;
cosj = cos(tt);
sinj = -sign*sin(tt);
d[0][j]= 0.5 * (cosj * (A[0][j]-A[0][N+j])
-sinj*(A[1][j] - A[1][N+j]));
}
fft1r2(c, d, -1);
for (k=0; k<N; k++)
{
dk = k+k;
A[0][dk]=c[0][k];
A[0][dk+1]=d[0][k];
A[1][dk]=0.0;
A[1][dk+1]=0.0;
}
} // end else
}
+63
View File
@@ -0,0 +1,63 @@
/**
* 一维卡尔曼滤波器
* 使用时先定义一个kalman指针,然后调用kalmanCreate()创建一个滤波器
* 每次读取到传感器数据后即可调用KalmanFilter()来对数据进行滤波
* 使用示例
* extKalman_t p; //定义一个卡尔曼滤波器结构体
* float SersorData; //需要进行滤波的数据
* KalmanCreate(&p,20,200); //初始化该滤波器的Q=20 R=200参数
* while(1)
* {
* SersorData = sersor(); //获取数据
* SersorData = KalmanFilter(&p,SersorData); //对数据进行滤波
* }
*/
#include "kalman.h"
/**
* @name kalmanCreate
* @brief 创建一个卡尔曼滤波器
* @param p: 滤波器
* T_Q:系统噪声协方差
* T_R:测量噪声协方差
*
* @retval none
* @attention R固定,Q越大,代表越信任侧量值,Q无穷代表只用测量值
* 反之,Q越小代表越信任模型预测值,Q为零则是只用模型预测
*/
void KalmanCreate(extKalman_t* p, float T_Q, float T_R)
{
p->X_last = (float)0;
p->P_last = 0;
p->Q = T_Q;
p->R = T_R;
p->A = 1;
p->B = 0;
p->H = 1;
p->X_mid = p->X_last;
}
/**
* @name KalmanFilter
* @brief 卡尔曼滤波器
* @param p: 滤波器
* dat:待滤波数据
* @retval 滤波后的数据
* @attention Z(k)是系统输入,即测量值 X(k|k)是卡尔曼滤波后的值,即最终输出
* A=1 B=0 H=1 I=1 W(K) V(k)是高斯白噪声,叠加在测量值上了,可以不用管
* 以下是卡尔曼的5个核心公式
* 一阶H'即为它本身,否则为转置矩阵
*/
float KalmanFilter(extKalman_t* p, float dat)
{
p->X_mid = p->A * p->X_last; //百度对应公式(1) x(k|k-1) = A*X(k-1|k-1)+B*U(k)+W(K)
p->P_mid = p->A * p->P_last + p->Q; //百度对应公式(2) p(k|k-1) = A*p(k-1|k-1)*A'+Q
p->kg = p->P_mid / (p->P_mid + p->R); //百度对应公式(4) kg(k) = p(k|k-1)*H'/(H*p(k|k-1)*H'+R)
p->X_now = p->X_mid + p->kg * (dat - p->X_mid); //百度对应公式(3) x(k|k) = X(k|k-1)+kg(k)*(Z(k)-H*X(k|k-1))
p->P_now = (1 - p->kg) * p->P_mid; //百度对应公式(5) p(k|k) = (I-kg(k)*H)*P(k|k-1)
p->P_last = p->P_now; //状态更新
p->X_last = p->X_now;
return p->X_now; //输出预测结果x(k|k)
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef _KALMAN_H
#define _KALMAN_H
typedef struct
{
float X_last; //上一时刻的最优结果 X(k-|k-1)
float X_mid; //当前时刻的预测结果 X(k|k-1)
float X_now; //当前时刻的最优结果 X(k|k)
float P_mid; //当前时刻预测结果的协方差 P(k|k-1)
float P_now; //当前时刻最优结果的协方差 P(k|k)
float P_last; //上一时刻最优结果的协方差 P(k-1|k-1)
float kg; //kalman增益
float A; //系统参数
float B;
float Q;
float R;
float H;
} extKalman_t;
void KalmanCreate(extKalman_t* p, float T_Q, float T_R);
float KalmanFilter(extKalman_t* p, float dat);
#endif
+354
View File
@@ -0,0 +1,354 @@
#ifdef _WIN32
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "user32.lib")
#define _USE_MATH_DEFINES
#include <cmath>
#else
#include <alsa/asoundlib.h>
#include <cmath>
#endif
#include <iostream>
#include <cstdint>
#include <vector>
#include <cmath>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef M_LN2
#define M_LN2 0.693147180559945309417
#endif
#ifdef _WIN32
// Windows 回调结构
struct PlaybackContext {
volatile bool done;
WAVEHDR* header;
};
// Windows 播放完成回调函数
void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
DWORD_PTR dwParam1, DWORD_PTR dwParam2) {
if (uMsg == WOM_DONE) {
PlaybackContext* ctx = reinterpret_cast<PlaybackContext*>(dwInstance);
ctx->done = true;
}
}
#endif
extern "C" {
#ifdef _WIN32
#define SND_EXPORT __declspec(dllexport)
#else
#define SND_EXPORT
#endif
SND_EXPORT bool play_frequency(float frequency, int32_t duration_ms,
double volume, int32_t channels,
int32_t sample_rate) {
// 参数验证
if (frequency <= 0.0f || duration_ms <= 0 ||
volume <= 0.0 || volume > 1.0 ||
channels <= 0 || channels > 8 ||
sample_rate <= 0) {
std::cerr << "Invalid parameters" << std::endl;
return false;
}
// 精确计算帧数:使用 round 而非截断
// duration_ms / 1000.0 得到秒数,乘以采样率得到帧数
int64_t num_frames = static_cast<int64_t>(
std::round(static_cast<double>(sample_rate) * duration_ms / 1000.0)
);
if (num_frames <= 0) {
std::cerr << "Duration too short to generate any frames" << std::endl;
return false;
}
// 计算实际播放时长(用于调试)
double actual_duration_ms = static_cast<double>(num_frames) / sample_rate * 1000.0;
std::cout << "Requested duration: " << duration_ms << " ms, "
<< "Actual duration: " << actual_duration_ms << " ms, "
<< "Frames: " << num_frames << std::endl;
// 计算总采样点数
int32_t total_samples = static_cast<int32_t>(num_frames * channels);
std::vector<int16_t> buffer(total_samples);
// 计算振幅(应用对数音量缩放以获得更自然的音量控制)
int32_t amplitude = static_cast<int32_t>(32767.0 * volume);
// 生成正弦波数据
for (int64_t i = 0; i < num_frames; i++) {
// 使用 double 计算以提高精度
double t = static_cast<double>(i) / sample_rate;
double value = sin(2.0 * M_PI * frequency * t);
// 应用淡入淡出以避免咔嗒声
int64_t fade_samples = static_cast<int64_t>(sample_rate * 0.005); // 5ms 淡入淡出
double fade_factor = 1.0;
if (i < fade_samples) {
// 淡入:从 0 到 1
fade_factor = static_cast<double>(i) / fade_samples;
} else if (i >= num_frames - fade_samples) {
// 淡出:从 1 到 0
fade_factor = static_cast<double>(num_frames - 1 - i) / fade_samples;
}
int16_t sample = static_cast<int16_t>(
amplitude * value * fade_factor
);
// 填充所有声道
for (int32_t ch = 0; ch < channels; ch++) {
buffer[i * channels + ch] = sample;
}
}
#ifdef _WIN32
// ===== Windows 实现(带回调,精确计时) =====
WAVEFORMATEX wfx;
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = channels;
wfx.nSamplesPerSec = sample_rate;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = channels * 2; // 16-bit = 2 bytes per sample
wfx.nAvgBytesPerSec = sample_rate * wfx.nBlockAlign;
wfx.cbSize = 0;
PlaybackContext context;
context.done = false;
context.header = new WAVEHDR();
// 打开波形设备,使用回调函数
HWAVEOUT hWaveOut;
MMRESULT result = waveOutOpen(
&hWaveOut,
WAVE_MAPPER,
&wfx,
reinterpret_cast<DWORD_PTR>(waveOutProc), // 回调函数
reinterpret_cast<DWORD_PTR>(&context), // 传递给回调的数据
CALLBACK_FUNCTION
);
if (result != MMSYSERR_NOERROR) {
std::cerr << "Cannot open audio device. Error code: " << result << std::endl;
delete context.header;
return false;
}
// 准备 WAVEHDR
WAVEHDR& header = *context.header;
ZeroMemory(&header, sizeof(WAVEHDR));
header.lpData = reinterpret_cast<char*>(buffer.data());
header.dwBufferLength = static_cast<DWORD>(total_samples * sizeof(int16_t));
header.dwFlags = 0;
header.dwLoops = 0;
result = waveOutPrepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
if (result != MMSYSERR_NOERROR) {
std::cerr << "Failed to prepare audio header. Error code: " << result << std::endl;
waveOutClose(hWaveOut);
delete context.header;
return false;
}
// 写入音频数据
result = waveOutWrite(hWaveOut, &header, sizeof(WAVEHDR));
if (result != MMSYSERR_NOERROR) {
std::cerr << "Failed to write audio data. Error code: " << result << std::endl;
waveOutUnprepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutClose(hWaveOut);
delete context.header;
return false;
}
// 等待播放完成(使用事件等待而非轮询)
auto start_time = timeGetTime();
while (!context.done) {
// 使用 MsgWaitForMultipleObjects 等待消息或超时
// 这样可以在等待的同时处理 Windows 消息
DWORD wait_result = MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT);
if (wait_result == WAIT_OBJECT_0) {
// 处理 Windows 消息
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// 超时检查(防止无限等待)
DWORD elapsed = timeGetTime() - start_time;
if (elapsed > static_cast<DWORD>(duration_ms + 1000)) {
std::cerr << "Playback timeout" << std::endl;
waveOutReset(hWaveOut);
break;
}
}
auto end_time = timeGetTime();
DWORD actual_play_time = end_time - start_time;
std::cout << "Actual playback time: " << actual_play_time << " ms" << std::endl;
// 清理
waveOutUnprepareHeader(hWaveOut, &header, sizeof(WAVEHDR));
waveOutClose(hWaveOut);
delete context.header;
#else
// ===== Linux ALSA 实现 =====
snd_pcm_t *pcm_handle = nullptr;
const char* device = "default";
int rc = snd_pcm_open(&pcm_handle, device, SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) {
std::cerr << "Cannot open device '" << device << "': "
<< snd_strerror(rc) << std::endl;
return false;
}
// 设置硬件参数
snd_pcm_hw_params_t *hw_params;
snd_pcm_hw_params_alloca(&hw_params);
rc = snd_pcm_hw_params_any(pcm_handle, hw_params);
if (rc < 0) {
std::cerr << "Cannot get hardware parameters: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
rc = snd_pcm_hw_params_set_access(pcm_handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (rc < 0) {
std::cerr << "Cannot set access type: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
rc = snd_pcm_hw_params_set_format(pcm_handle, hw_params,
SND_PCM_FORMAT_S16_LE);
if (rc < 0) {
std::cerr << "Cannot set sample format: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
rc = snd_pcm_hw_params_set_channels(pcm_handle, hw_params, channels);
if (rc < 0) {
std::cerr << "Cannot set channel count: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
unsigned int hw_sample_rate = static_cast<unsigned int>(sample_rate);
rc = snd_pcm_hw_params_set_rate_near(pcm_handle, hw_params,
&hw_sample_rate, 0);
if (rc < 0) {
std::cerr << "Cannot set sample rate: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
if (hw_sample_rate != static_cast<unsigned int>(sample_rate)) {
std::cout << "Requested sample rate " << sample_rate
<< " Hz not supported, using " << hw_sample_rate << " Hz" << std::endl;
}
// 设置缓冲区大小和周期
snd_pcm_uframes_t buffer_size;
rc = snd_pcm_hw_params_get_buffer_size_max(hw_params, &buffer_size);
if (buffer_size > static_cast<snd_pcm_uframes_t>(num_frames)) {
snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hw_params,
&reinterpret_cast<snd_pcm_uframes_t&>(num_frames));
}
rc = snd_pcm_hw_params(pcm_handle, hw_params);
if (rc < 0) {
std::cerr << "Cannot set hardware parameters: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
// 设置软件参数
snd_pcm_sw_params_t *sw_params;
snd_pcm_sw_params_alloca(&sw_params);
rc = snd_pcm_sw_params_current(pcm_handle, sw_params);
if (rc < 0) {
std::cerr << "Cannot get software parameters: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
rc = snd_pcm_sw_params_set_start_threshold(pcm_handle, sw_params,
static_cast<snd_pcm_uframes_t>(num_frames));
if (rc < 0) {
std::cerr << "Cannot set start threshold: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
rc = snd_pcm_sw_params(pcm_handle, sw_params);
if (rc < 0) {
std::cerr << "Cannot set software parameters: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
// 播放音频(写入数据)
snd_pcm_sframes_t frames_written = 0;
snd_pcm_uframes_t frames_to_write = static_cast<snd_pcm_uframes_t>(num_frames);
// 循环写入直到所有数据都播放完毕
while (frames_written < static_cast<snd_pcm_sframes_t>(num_frames)) {
snd_pcm_uframes_t remaining = static_cast<snd_pcm_uframes_t>(num_frames - frames_written);
rc = snd_pcm_writei(pcm_handle,
buffer.data() + (frames_written * channels),
remaining);
if (rc == -EPIPE) {
// 缓冲区下溢,需要恢复
std::cerr << "Buffer underrun occurred" << std::endl;
snd_pcm_prepare(pcm_handle);
rc = snd_pcm_writei(pcm_handle,
buffer.data() + (frames_written * channels),
remaining);
}
if (rc < 0) {
std::cerr << "Error writing to PCM device: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
if (rc > 0) {
frames_written += rc;
}
}
// 等待所有数据播放完成
rc = snd_pcm_drain(pcm_handle);
if (rc < 0) {
std::cerr << "Error draining PCM device: " << snd_strerror(rc) << std::endl;
snd_pcm_close(pcm_handle);
return false;
}
snd_pcm_close(pcm_handle);
#endif
return true;
}
} // extern "C"
+12
View File
@@ -0,0 +1,12 @@
#pragma once
#include <cuda_runtime.h>
#include <stdio.h>
#define CHECK(call) do {\
cudaError_t err = call;\
if (err != cudaSuccess) {\
fprintf(stderr, "CUDA error at %s:%d\n", __FILE__, __LINE__);\
fprintf(stderr, "%s\n", cudaGetErrorString(err));\
exit(EXIT_FAILURE);\
}\
} while (0)
+872
View File
@@ -0,0 +1,872 @@
#include <cstdint>
#include <stdint.h>
#include <time.h>
#include <random>
#include "error.cuh"
extern "C" {
/* 创建一个设备数组 */
static void* _crtDevArr(const size_t m)
{
void *d;
CHECK(cudaMalloc(&d, m));
return d;
}
/* 释放一个设备数组 */
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_Free(void* d)
#else
void Cu4cj_Free(void* d)
#endif
{
CHECK(cudaFree(d));
}
/* 创建一个单精度浮点数组 输入数组的长度(不初始化) */
#ifdef _WIN64
__declspec(dllexport) void* Cu4cj_Orig(uint64_t n)
#else
void* Cu4cj_Orig(int64_t n)
#endif
{
return _crtDevArr(n * sizeof(float));
}
/*
* 从主机内存拷贝数据到设备内存
* FIXME: 是否需要做成输入目的地址
*/
#ifdef _WIN64
__declspec(dllexport) float* Cu4cj_ToDev(float* h, uint64_t n)
#else
float* Cu4cj_ToDev(float* h, int64_t n)
#endif
{
float* d = (float*)Cu4cj_Orig(n);
CHECK(cudaMemcpy(d, h, n * sizeof(float), cudaMemcpyHostToDevice));
return d;
}
/* 从设备内存拷贝数据到主机内存 */
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_ToHost(float* d, float* h, const uint64_t n)
#else
void Cu4cj_ToHost(float* d, float* h, int64_t n)
#endif
{
CHECK(cudaMemcpy(h, d, n * sizeof(float), cudaMemcpyDeviceToHost));
}
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_Copy(float* d, float* h, const uint64_t n)
#else
void Cu4cj_Copy(float* d, float* h, int64_t n)
#endif
{
CHECK(cudaMemcpy(h, d, n * sizeof(float), cudaMemcpyDeviceToHost));
}
/* 创建一个单精度浮点数组并将其初始化为零
* FIXME: 当n == 10000 时进程被杀死 */
#ifdef _WIN64
__declspec(dllexport) float* Cu4cj_Zero(const uint64_t n)
#else
float* Cu4cj_Zero(int64_t n)
#endif
{
float* d = (float*)Cu4cj_Orig(n);
CHECK(cudaMemset(d, 0, n*sizeof(float)));
return d;
}
/* 正态分布初始化(适用于神经网络权重) */
#ifdef _WIN64
__declspec(dllexport) float* Cu4cj_Normal(uint64_t n, uint64_t fan_in, uint64_t fan_out)
#else
float* Cu4cj_Normal(int64_t n, int64_t fan_in, int64_t fan_out)
#endif
{
// 计算 Xavier/Glorot 初始化的标准差
float std = sqrt(2.0f / (fan_in + fan_out));
// 先在主机上分配内存并生成正态分布随机数
float* h = (float*)malloc(n * sizeof(float));
/* 初始化随机数种子 */
srand(time(NULL));
for (size_t i = 0; i < n; i++) {
// 使用 Box-Muller 变换生成正态分布随机数
float u1 = (float)rand() / RAND_MAX;
float u2 = (float)rand() / RAND_MAX;
float z = sqrt(-2.0f * log(u1)) * cos(2.0f * 3.1415926535f * u2);
h[i] = z * std;
}
// 在设备上分配内存
float* d = (float*)Cu4cj_Orig(n);
// 将主机内存中的数据拷贝到设备内存
CHECK(cudaMemcpy(d, h, n * sizeof(float), cudaMemcpyHostToDevice));
// 释放主机内存
free(h);
return d;
}
// 向量加法
static void __global__ _devAdd(float* d1, float* d2, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] + d2[i];
}
}
// 向量加法
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_Add(float* d1, float* d2, float* d_r, uint64_t n)
#else
void Cu4cj_Add(float* d1, float* d2, float* d_r, int64_t n)
#endif
{
_devAdd<<<(n + 255) / 256, 256>>>(d1, d2, d_r, n);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
}
// 向量减法
static void __global__ _devSub(float* d1, float* d2, float* r, int64_t n)
{
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] - d2[i];
}
}
// 向量减法
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_Sub(float* d1, float* d2, float* d_r, uint64_t n)
#else
void Cu4cj_Sub(float* d1, float* d2, float* d_r, int64_t n)
#endif
{
_devSub<<<(n + 255) / 256, 256>>>(d1, d2, d_r, n);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
}
// 向量依次相乘
static void __global__ _devMul(float* d1, float* d2, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] * d2[i];
}
}
// 向量依次相乘
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_Mul(float* d1, float* d2, float* d_r, uint64_t n)
#else
void Cu4cj_Mul(float* d1, float* d2, float* d_r, int64_t n)
#endif
{
_devMul<<<(n + 255) / 256, 256>>>(d1, d2, d_r, n);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
}
// 向量依次相除
static void __global__ _devDiv(float* d1, float* d2, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] / d2[i];
}
}
// 向量依次相除
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_Div(float* d1, float* d2, float* d_r, uint64_t n)
#else
void Cu4cj_Div(float* d1, float* d2, float* d_r, int64_t n)
#endif
{
_devDiv<<<(n + 255) / 256, 256>>>(d1, d2, d_r, n);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
}
// 向量加法广播
static void __global__ _devAddBroadcast(float* d1, float a, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] + a;
}
}
// 加法广播
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_AddBroadcast(float* d1, float a, float* d_r, uint64_t n)
#else
void Cu4cj_AddBroadcast(float* d1, float a, float* d_r, int64_t n)
#endif
{
_devAddBroadcast<<<(n + 255) / 256, 255>>>(d1, a, d_r, n);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
}
// 减法广播
static void __global__ _devSubBroadcast(float* d1, float a, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] - a;
}
}
// 减法广播
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_SubBroadcast(float* d1, float a, float* d_r, uint64_t n)
#else
void Cu4cj_SubBroadcast(float* d1, float a, float* d_r, int64_t n)
#endif
{
_devSubBroadcast<<<(n + 255) / 256, 256>>>(d1, a, d_r, n);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
}
// 乘法广播
static void __global__ _devMulBroadcast(float* d1, float a, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] * a;
}
}
// 乘法广播
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_MulBroadcast(float* d1, float a, float* d_r, uint64_t n)
#else
void Cu4cj_MulBroadcast(float* d1, float a, float* d_r, int64_t n)
#endif
{
_devMulBroadcast<<<(n + 255) / 256, 256>>>(d1, a, d_r, n);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
}
// 除法广播
static void __global__ _devDivBroadcast(float* d1, float a, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] / a;
}
}
// 除法广播
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_DivBroadcast(float* d1, float a, float* d_r, uint64_t n)
#else
void Cu4cj_DivBroadcast(float* d1, float a, float* d_r, int64_t n)
#endif
{
_devDivBroadcast<<<(n + 255) / 256, 256>>>(d1, a, d_r, n);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
}
// 矩阵乘法
static void __global__ _devMatDot(float* d1, float* d2, float* r, int64_t m, int64_t n, int64_t k) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
int j = blockIdx.y * blockDim.y + threadIdx.y;
if (i < m && j < k) {
float sum = 0.0f;
for (int p = 0; p < n; p++) {
sum += d1[i * n + p] * d2[p * k + j];
}
r[i * k + j] = sum;
}
}
// 矩阵乘法
// m、n、k 分别为矩阵 A、B、C 的行数、列数
// 其中 n 为矩阵 A 的列数,也为矩阵 B 的行数
#ifdef _WIN64
__declspec(dllexport) void Cu4cj_MatDot(float* d1, float* d2, float* d_r, uint64_t m, uint64_t n, uint64_t k)
#else
void Cu4cj_MatDot(float* d1, float* d2, float* d_r, int64_t m, int64_t n, int64_t k)
#endif
{
dim3 blockDim(16, 16);
dim3 gridDim((m + blockDim.x - 1) / blockDim.x, (k + blockDim.y - 1) / blockDim.y);
_devMatDot<<<gridDim, blockDim>>>(d1, d2, d_r, m, n, k);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
}
// 矩阵转置
static void __global__ _devTransport(float* d1, float* d2, int64_t m, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
int j = blockIdx.y * blockDim.y + threadIdx.y;
if (i < m && j < n) {
d2[j * m + i] = d1[i * n + j];
}
}
#ifdef _WIN64
__declspec(dllexport) float* Cu4cj_Transport(float *m, uint64_t r, uint64_t c)
#else
float* Cu4cj_Transport(float *m, uint64_t r, uint64_t c)
#endif
{
float *d_r = (float*)Cu4cj_Zero(c * r * sizeof(float));
_devTransport<<<1, c * r>>>(m, d_r, c, r);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
return d_r;
}
// Sigmoid 激活函数,输入为矩阵,输出为矩阵
static void __global__ _devSigmoid(float* d1, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = 1.0f / (1.0f + exp(-d1[i]));
}
}
// Sigmoid 导数
static void __global__ _devSigmoidDerivative(float* d1, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] * (1.0f - d1[i]);
}
}
// ReLU
static void __global__ _devReLU(float* d1, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] > 0.0f ? d1[i] : 0.0f;
}
}
// ReLU 导数
static void __global__ _devReLUDerivative(float* d1, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] > 0.0f ? 1.0f : 0.0f;
}
}
// Leaky_ReLU
static void __global__ _devLeakyReLU(float* d1, float* r, int64_t n, float a) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] > 0.0f ? d1[i] : a * d1[i];
}
}
// Leaky_ReLU 导数
static void __global__ _devLeakyReLU_Derivative(float* d1, float* r, int64_t n, float a) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = d1[i] > 0.0f ? 1.0f : a;
}
}
// Tanh 激活函数,输入为矩阵,输出为矩阵
static void __global__ _devTanh(float* d1, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = (exp(d1[i]) - exp(-d1[i])) / (exp(d1[i]) + exp(-d1[i]));
}
}
// Tanh 导数
static void __global__ _devTanhDerivative(float* d1, float* r, int64_t n) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i < n) {
r[i] = 1.0f - d1[i] * d1[i];
}
}
}
namespace qing {
class Matrix {
public:
Matrix(uint64_t r, uint64_t c) {
this->r = r;
this->c = c;
this->d = (float*)Cu4cj_Zero(r * c * sizeof(float));
}
Matrix(uint64_t r, uint64_t c, float* d) {
this->r = r;
this->c = c;
this->d = d;
}
Matrix(const Matrix& m) {
this->r = m.getR();
this->c = m.getC();
this->d = (float*)Cu4cj_Zero(m.getR() * m.getC() * sizeof(float));
Cu4cj_Copy(m.getD(), d, m.getR() * m.getC() * sizeof(float));
}
~Matrix() {
Cu4cj_Free(d);
}
uint64_t getR() const {
return r;
}
uint64_t getC() const {
return c;
}
float* getD() const {
return d;
}
float get(uint64_t r, uint64_t c) const {
float h;
CHECK(cudaMemcpy(&h, &d[c * this->r + r], sizeof(float), cudaMemcpyDeviceToHost));
return h;
}
void set(uint64_t r, uint64_t c, float v) {
CHECK(cudaMemcpy(&d[c * this->r + r], &v, sizeof(float), cudaMemcpyHostToDevice));
}
bool hasSameShape(const Matrix& m) {
return r == m.getR() && c == m.getC();
}
bool hasDotShape(const Matrix& m) {
return c == m.getR();
}
Matrix operator+(const Matrix& m) {
float *d_r = (float*)Cu4cj_Zero(r * c * sizeof(float));
Cu4cj_Add(d, m.getD(), d_r, r * c);
return Matrix(r, c, d_r);
}
Matrix operator+(float a) {
float *d_r = (float*)Cu4cj_Zero(r * c * sizeof(float));
Cu4cj_AddBroadcast(d, a, d_r, r * c);
return Matrix(r, c, d_r);
}
Matrix operator-(const Matrix& m) {
float *d_r = (float*)Cu4cj_Zero(r * c * sizeof(float));
Cu4cj_Sub(d, m.getD(), d_r, r * c);
return Matrix(r, c, d_r);
}
Matrix operator-(float a) {
float *d_r = (float*)Cu4cj_Zero(r * c * sizeof(float));
Cu4cj_SubBroadcast(d, a, d_r, r * c);
return Matrix(r, c, d_r);
}
Matrix operator*(const Matrix& m) {
float *d_r = (float*)Cu4cj_Zero(r * c * sizeof(float));
Cu4cj_Mul(d, m.getD(), d_r, r * c);
return Matrix(r, c, d_r);
}
Matrix operator*(float a) {
float *d_r = (float*)Cu4cj_Zero(r * c * sizeof(float));
Cu4cj_MulBroadcast(d, a, d_r, r * c);
return Matrix(r, c, d_r);
}
Matrix operator/(const Matrix& m) {
float *d_r = (float*)Cu4cj_Zero(r * c * sizeof(float));
Cu4cj_Div(d, m.getD(), d_r, r * c);
return Matrix(r, c, d_r);
}
Matrix operator/(float a) {
float *d_r = (float*)Cu4cj_Zero(r * c * sizeof(float));
Cu4cj_DivBroadcast(d, a, d_r, r * c);
return Matrix(r, c, d_r);
}
Matrix dot(const Matrix& m) {
//if (!hasDotShape(m)) {
// throw std::invalid_argument("Dot product: matrices must have compatible shapes");
//}
float *d_r = (float*)Cu4cj_Zero(r * m.getC() * sizeof(float));
Cu4cj_MatDot(d, m.getD(), d_r, r, c, m.getC());
return Matrix(r, m.getC(), d_r);
}
Matrix T() {
float *d_r = (float*)Cu4cj_Transport(d, r, c);
return Matrix(c, r, d_r);
}
private:
uint64_t r, c;
float* d;
};
}
using namespace qing;
extern "C" {
#ifdef _WIN64
__declspec(dllexport) void* createMatrix(const uint64_t r, const uint64_t c)
#else
void* createMatrix(int64_t r, int64_t c)
#endif
{
return(void*) new Matrix(r, c);
}
#ifdef _WIN64
__declspec(dllexport) void freeMatrix(void* m)
#else
void freeMatrix(void* m)
#endif
{
delete (Matrix*)m;
}
#ifdef _WIN64
__declspec(dllexport) void* xavierNormalInit(uint64_t fan_in, uint64_t fan_out)
#else
void* xavierNormalInit(uint64_t fan_in, uint64_t fan_out)
#endif
{
float* d = Cu4cj_Normal(fan_in * fan_out, fan_in, fan_out);
return (void *)new Matrix(fan_in, fan_out, d);
}
#ifdef _WIN64
__declspec(dllexport) void* xavierUniformInit(uint64_t fan_in, uint64_t fan_out)
#else
void* xavierUniformInit(uint64_t fan_in, uint64_t fan_out)
#endif
{
uint64_t n = fan_in * fan_out;
// 先在主机上分配内存并生成正态分布随机数
float* h = (float*)malloc(n * sizeof(float));
/* 使用高质量的随机数生成器 */
static std::mt19937 generator(std::random_device{}());
/* 计算区间范围:sqrt(2 / (fan_in + fan_out)) */
float scale = std::sqrt(2.0f / (fan_in + fan_out));
/* 创建均匀分布对象,范围为[-scale, scale] */
std::uniform_real_distribution<float> distribution(-scale, scale);
/* 遍历权重矩阵,进行赋值 */
uint64_t k=0;
for (uint64_t i=0; i<fan_in; ++i)
for (uint64_t j=0; j<fan_out; ++j)
h[k++] = distribution(generator);
// 在设备上分配内存
float* d = (float*)Cu4cj_Orig(n*sizeof(float));
// 将主机内存中的数据拷贝到设备内存
CHECK(cudaMemcpy(d, h, n * sizeof(float), cudaMemcpyHostToDevice));
// 释放主机内存
free(h);
return (void *)new Matrix(fan_in, fan_out, d);
}
#ifdef _WIN64
__declspec(dllexport) uint64_t getrowMatrix(void *m)
#else
uint64_t getrowMatrix(void *m)
#endif
{
Matrix* m1 = (Matrix*)m;
return m1->getR();
}
#ifdef _WIN64
__declspec(dllexport) uint64_t getcolMatrix(void *m)
#else
uint64_t getcolMatrix(void *m)
#endif
{
Matrix* m1 = (Matrix*)m;
return m1->getC();
}
#ifdef _WIN64
__declspec(dllexport) float getMatrix(void *m, uint64_t r, uint64_t c)
#else
float getMatrix(void *m, uint64_t r, uint64_t c)
#endif
{
Matrix* m1 = (Matrix*)m;
return m1->get(r, c);
}
#ifdef _WIN64
__declspec(dllexport) void setMatrix(void *m, uint64_t r, uint64_t c, float v)
#else
void setMatrix(void *m, uint64_t r, uint64_t c, float v)
#endif
{
Matrix* m1 = (Matrix*)m;
m1->set(r, c, v);
}
#ifdef _WIN64
__declspec(dllexport) bool sameShapeMatrix(void *m, void *s)
#else
bool sameShapeMatrix(void *m, void *s)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix* s1 = (Matrix*)s;
return (*m1).hasSameShape(*s1);
}
#ifdef _WIN64
__declspec(dllexport) bool dotShapeMatrix(void *m, void *s)
#else
bool dotShapeMatrix(void *m, void *s)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix* s1 = (Matrix*)s;
return (*m1).hasDotShape(*s1);
}
#ifdef _WIN64
__declspec(dllexport) void* plusMatrix(void *m, void *s)
#endif
#ifdef __linux__
void* plusMatrix(void *m, void *s)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix* s1 = (Matrix*)s;
Matrix r = *m1 + *s1;
return (void *)new Matrix(r);
}
#ifdef _WIN64
__declspec(dllexport) void* plusBroadcastMatrix(void *m, float a)
#else
void* plusBroadcastMatrix(void *m, float a)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix r = *m1 + a;
return (void *)new Matrix(r);
}
#ifdef _WIN64
__declspec(dllexport) void* subMatrix(void *m, void *s)
#else
void* subMatrix(void *m, void *s)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix* s1 = (Matrix*)s;
Matrix r = *m1 - *s1;
return (void *)new Matrix(r);
}
#ifdef _WIN64
__declspec(dllexport) void* subBroadcastMatrix(void *m, float a)
#else
void* subBroadcastMatrix(void *m, float a)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix r = *m1 - a;
return (void *)new Matrix(r);
}
#ifdef _WIN64
__declspec(dllexport) void* multiMatrix(void *m, void *s)
#else
void* multiMatrix(void *m, void *s)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix* s1 = (Matrix*)s;
Matrix r = (*m1) * (*s1);
return (void *)new Matrix(r);
}
#ifdef _WIN64
__declspec(dllexport) void* multiBroadcastMatrix(void *m, float a)
#else
void* multiBroadcastMatrix(void *m, float a)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix r = (*m1) * a;
return (void *)new Matrix(r);
}
#ifdef _WIN64
__declspec(dllexport) void* divMatrix(void *m, void *s)
#else
void* divMatrix(void *m, void *s)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix* s1 = (Matrix*)s;
Matrix r = (*m1) / (*s1);
return (void *)new Matrix(r);
}
#ifdef _WIN64
__declspec(dllexport) void* divBroadcastMatrix(void *m, float a)
#else
void* divBroadcastMatrix(void *m, float a)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix r = (*m1) / a;
return (void *)new Matrix(r);
}
#ifdef _WIN64
__declspec(dllexport) void* dotMatrix(void *m, void *s)
#else
void* dotMatrix(void *m, void *s)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix* s1 = (Matrix*)s;
Matrix r = (*m1).dot(*s1);
return (void *)new Matrix(r);
}
#ifdef _WIN64
__declspec(dllexport) void* transposeMatrix(void *m)
#endif
#ifdef __linux__
void* transposeMatrix(void *m)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix r = m1->T();
return (void *)new Matrix(r);
}
#ifdef _WIN64
__declspec(dllexport) void* Sigmoid_do(void *m)
#else
void* Sigmoid_do(void *m)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix s = Matrix(*m1);
_devSigmoid<<<1, s.getR()* s.getC()>>>(s.getD(), s.getD(), s.getR()* s.getC());
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
return (void *)new Matrix(s);
}
#ifdef _WIN64
__declspec(dllexport) void* Sigmoid_dot(void *m)
#else
void* Sigmoid_dot(void *m)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix s = Matrix(*m1);
_devSigmoidDerivative<<<1, s.getR()* s.getC()>>>(s.getD(), s.getD(), s.getR()* s.getC());
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
return (void *)new Matrix(s);
}
#ifdef _WIN64
__declspec(dllexport) void* ReLU_do(void *m)
#else
void* ReLU_do(void *m)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix s = Matrix(*m1);
_devReLU<<<1, s.getR()* s.getC()>>>(s.getD(), s.getD(), s.getR()* s.getC());
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
return (void *)new Matrix(s);
}
#ifdef _WIN64
__declspec(dllexport) void* ReLU_dot(void *m)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix s = Matrix(*m1);
_devReLUDerivative<<<1, s.getR()* s.getC()>>>(s.getD(), s.getD(), s.getR()* s.getC());
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
return (void *)new Matrix(s);
}
#ifdef _WIN64
__declspec(dllexport) void* Leaky_ReLU_do(void *m, float a)
#else
void* LeakyReLU_do(void *m, float a)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix s = Matrix(*m1);
_devLeakyReLU<<<1, s.getR()* s.getC()>>>(s.getD(), s.getD(), s.getR()* s.getC(), a);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
return (void *)new Matrix(s);
}
#ifdef _WIN64
__declspec(dllexport) void* Leaky_ReLU_dot(void *m, float a)
#else
void* LeakyReLU_dot(void *m, float a)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix s = Matrix(*m1);
_devLeakyReLU_Derivative<<<1, s.getR()* s.getC()>>>(s.getD(), s.getD(), s.getR()* s.getC(), a);
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
return (void *)new Matrix(s);
}
// Tanh
#ifdef _WIN64
__declspec(dllexport) void* Tanh_do(void *m)
#else
void* Tanh_do(void *m)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix s = Matrix(*m1);
_devTanh<<<1, s.getR()* s.getC()>>>(s.getD(), s.getD(), s.getR()* s.getC());
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
return (void *)new Matrix(s);
}
#ifdef _WIN64
__declspec(dllexport) void* Tanh_dot(void *m)
#else
void* Tanh_dot(void *m)
#endif
{
Matrix* m1 = (Matrix*)m;
Matrix s = Matrix(*m1);
_devTanhDerivative<<<1, s.getR()* s.getC()>>>(s.getD(), s.getD(), s.getR()* s.getC());
CHECK(cudaGetLastError());
CHECK(cudaDeviceSynchronize());
return (void *)new Matrix(s);
}
}
+339
View File
@@ -0,0 +1,339 @@
/* TODO(20260518-qing): 稍后我们专门做一个i2c代码仓库,这样就能够区分不同架构上面的int的大小
* TODO(20260615-qing): 有没有可能只做一个简单的封装不引入异常机制 */
package bot4cj.hd
import pthread4cj.*
import ctype4cj.*
/* -------- 我们将直接调用glibc中的一些函数 -------- */
foreign func ioctl(fd: C_Int, addr: C_ULong, ...): C_Int /* -------- 成功返回非负整数,失败返回-1 -------- */
const I2C_FAILED = -1i32 /* -------- I2C关闭文件失败 -------- */
const I2C_SUCCESS = 0i32 /* -------- I2C关闭文件成功 -------- */
const I2C_SLAVE = 0x0703u64 /* -------- at least on arm64 -------- */
/* -------- OPEN I2C DEVICE 打开I2C设备 -------- */
@C
public func openI2cDev(path: CString): Int32 {
unsafe {
let fd = libc_open(path, O_RDWR)
if (fd > 0) {
I2cLock_create(fd) /* -------- 为I2C设备创建锁 -------- */
return fd
}
else {
throw I2cOpenedFailed("?")
}
}
}
/* ---- 简化版,判断在外部实现 ---- */
@C
public func openI2cDev1(path: CString): C_Int {
unsafe {
let fd = libc_open(path, O_RDWR)
I2cLock_create(fd)
return fd
}
}
/* -------- CLOSE I2C DEVICE 关闭I2C设备 -------- */
@C
public func closeI2cDev(fd: Int32): Unit {
unsafe {
I2cLock_destroy(fd) /* -------- 销毁I2C设备的锁 -------- */
let ret = libc_close(fd)
if (ret == I2C_FAILED) {
throw I2cClosedFailed("?") }
else if (ret == I2C_SUCCESS) { ; }
else {
throw I2cUnknowExp(
"?", "$function returned: {ret.toString()}")
}
}
}
@C
public func closeI2cDev1(fd: C_Int): Unit {
unsafe {
I2cLock_destroy(fd)
libc_close(fd)
}
}
/* -------- SET I2C ADDR 设置I2C地址 -------- */
@C
public func setI2cAddr(fd: Int32, addr: UInt8): Unit {
unsafe {
let ret = ioctl(fd, I2C_SLAVE, addr)
if (ret < 0) {
throw I2cSetAddressFailed("?") }
}
}
@C
public func setI2cAddr1(fd: C_Int, addr: UInt8): C_Int {
unsafe {
return ioctl(fd, I2C_SLAVE, addr)
}
}
/* -------- WRITE I2C REG 写I2C寄存器 -------- */
@C
public func writeI2cReg(fd: Int32, reg: UInt8, val:UInt8): Unit {
var a: VArray<UInt8, $2> = [reg, val]
var p: CPointer<Unit> = unsafe { CPointer<Unit>(inout a) }
if (2 != unsafe { libc_write(fd, p, 2) }) {
throw I2cWriteRegFailed("?")
}
}
/* -------- READ I2C REG 读I2C寄存器 --------
* INFO(20260522-qing): 读取寄存器之前要先写寄存器地址
* INFO(20260616-qing): 这个函数只读取一个字节的数据
* -------- */
@C
public func readI2cReg(fd: Int32, reg: UInt8): Int8 {
var ptr = reg
if (1 != unsafe { libc_write(fd, CPointer<Unit>(inout ptr), 1) }) {
throw I2cReadRegFailed("?")
}
var value: Int8 = 0
var p = unsafe { CPointer<Unit>(inout value) }
let ret = unsafe { libc_read(fd, p, 1) }
if (ret != 1) { throw I2cReadRegFailed("?") }
return value
}
/* -------- I2C锁具 --------
* INFO(20260521-qing): 因为每个I2C接口在通信时具有原子化的需求
* - 需要一个互斥锁集合,在打开第一个I2C设备时被初始化,
* - 释放最后一个I2C设备时,将锁具释放
* - **每次进行I2C通信时,进行上锁操作** */
@C
struct I2C_Lock {
var i2c_fd: Int32 = 0 /* -------- 这个锁绑定的I2C设备文件句柄 -------- */
var lock = CPointer<UInt8>() /* -------- phread_mutex_t 锁对象 -------- */
var is_active = false /* -------- 该锁具是否被激活 --------*/
}
var MAIN_LOCK = CPointer<UInt8>() /* -------- 主锁,操作锁具时使用 -------- */
var LOCKS = CPointer<I2C_Lock>() /* -------- 指向锁具内存的指针 -------- */
const LOCKS_SIZE: Int64 = 20 /* -------- 锁具的容量 --------*/
/* -------- 为一个I2C设备注册锁具 -------- */
@C
func I2cLock_create(fd: Int32): Unit {
unsafe {
/* -------- 初始化主要的锁 -------- */
if (MAIN_LOCK.isNull()) {
MAIN_LOCK = _mutex_create()
LOCKS = LibC.malloc<I2C_Lock>(count: LOCKS_SIZE)
for (i in 0..20) {
let tmp = I2C_Lock()
(LOCKS+i).write(tmp)
}
}
/* -------- 获取主锁 --------*/
_mutex_lock(MAIN_LOCK)
var flag = false
for (idx in 0..LOCKS_SIZE) {
var lock = (LOCKS+idx).read()
if (lock.is_active == false) {
lock.is_active = true
lock.i2c_fd = fd
lock.lock = _mutex_create()
_mutex_init(lock.lock)
(LOCKS+idx).write(lock)
flag = true
break
}
}
_mutex_unlock(MAIN_LOCK)
if (flag ) { return } /* -------- 出口 -------- */
throw OverflowException("LOCKS is over.")
}
}
/* -------- 为一个I2C设备注销锁具 -------- */
@C
func I2cLock_destroy(fd: Int32): Unit {
unsafe {
/* -------- 检查锁具有没有被初始化 --------*/
if (MAIN_LOCK.isNull()) {
throw NoneValueException("no i2c lock")
}
_mutex_lock(MAIN_LOCK)
var flag_any = false
var flag_target = false
for (idx in 0..LOCKS_SIZE) {
var lock = (LOCKS+idx).read()
if (lock.is_active == true && lock.i2c_fd == fd) {
lock.is_active = false
_mutex_destroy(lock.lock)
_mutex_free(lock.lock)
(LOCKS+idx).write(lock)
flag_target = true
/* -------- NOTE: 扫描整个锁具不终止 -------- */
}
else if (lock.is_active == true && lock.i2c_fd != fd) {
flag_any = true
}
}
_mutex_unlock(MAIN_LOCK)
/* -------- 如果锁具里已经没有I2C锁, 就释放整个锁具 -------- */
if (!flag_any) {
LibC.free(LOCKS)
_mutex_free(MAIN_LOCK)
MAIN_LOCK = CPointer<UInt8>()
}
/* -------- 出口 -------- */
if (flag_target) {
return }
throw NoneValueException("i2c lock nor found")
}
}
/* -------- 为一个I2C锁具加锁 -------- */
@C
func I2cLock_lock(fd: Int32): Unit {
unsafe {
if (MAIN_LOCK.isNull()) {
throw NoneValueException("no i2c lock")
}
_mutex_lock(MAIN_LOCK)
var flag = false
for (idx in 0..LOCKS_SIZE) {
var lock = (LOCKS+idx).read()
if (lock.is_active == true && lock.i2c_fd == fd) {
_mutex_lock(lock.lock)
(LOCKS+idx).write(lock)
flag = true
break
}
}
_mutex_unlock(MAIN_LOCK)
if (flag) { return }
throw NoneValueException("i2c lock nor found")
}
}
/* -------- 为一个I2C锁具解锁 -------- */
@C
func I2cLock_unlock(fd: Int32): Unit {
unsafe {
if (MAIN_LOCK.isNull()) {
throw NoneValueException("no i2c lock")
}
_mutex_lock(MAIN_LOCK)
var flag = false
for (idx in 0..LOCKS_SIZE) {
var lock = (LOCKS+idx).read()
if (lock.is_active == true && lock.i2c_fd == fd) {
_mutex_unlock(lock.lock)
(LOCKS+idx).write(lock)
flag = true
break
}
}
_mutex_unlock(MAIN_LOCK)
if (flag) {return}
throw NoneValueException("i2c lock nor found")
}
}
/* -------- 原生I2C通信类--------
* INFO(20260521-qing): 直接调用glibc的接口 */
public class I2C_Orig {
private var fd: Int32 = -1
private var path: String
public init (path: String) {
this.path = path
let s1 = unsafe {
LibC.mallocCString(path) }
fd = unsafe { openI2cDev(s1) }
}
public func _close(): Unit {
unsafe { closeI2cDev(fd) }
}
public func setAddr(addr: UInt8) {
unsafe { setI2cAddr(fd, addr) }
}
public func writeReg(reg: UInt8, val:UInt8): Unit {
unsafe { writeI2cReg(fd, reg, val) }
}
public func readReg(reg: UInt8): Int8 {
var value = unsafe { readI2cReg(fd, reg) }
return value
}
/*--------获取文件句柄--------*/
public func getFd(): Int32 {
return fd
}
}
/* -------- I2C设备异常 -------- */
open private class I2cExcept <: Exception {
public I2cExcept(dev: String, msg: String) {
super('${dev}: ${msg}') }
}
/* -------- 打开I2C设备失败 -------- */
private class I2cOpenedFailed <: I2cExcept {
public I2cOpenedFailed(dev: String) {
super(dev, "failed to open i2c device") }
}
/* -------- 关闭I2C设备失败 -------- */
private class I2cClosedFailed <: I2cExcept {
public I2cClosedFailed(dev: String) {
super(dev, "failed to close i2c device") }
}
/* -------- I2C设置地址失败 -------- */
private class I2cSetAddressFailed <: I2cExcept {
public I2cSetAddressFailed(dev: String) {
super(dev, "failed to open i2c device") }
}
/* -------- I2C写从机I2C失败 -------- */
private class I2cWriteRegFailed <: I2cExcept {
public I2cWriteRegFailed(dev: String) {
super(dev, "failed to write i2c reg") }
}
/* -------- I2C读从机I2C失败 -------- */
private class I2cReadRegFailed <: I2cExcept {
public I2cReadRegFailed(dev: String) {
super(dev, "failed to read i2c reg") }
}
/* -------- 未知I2C异常 -------- */
private class I2cUnknowExp <: I2cExcept {
public I2cUnknowExp(dev: String, msg: String) {
super(dev, msg) }
}
+243
View File
@@ -0,0 +1,243 @@
/* ----------------
* MPU6050陀螺仪与加速度计模块
* -
* TODO(20260516-qing): 陀螺仪需要卡尔曼滤波、通过四元数等方式获取姿态角
* - 所以应该使用一个C/C++线程来持续地采样,并且留一个接口进行获取
*/
package bot4cj.hd
import std.time.*
import std.math.*
import pthread4cj.*
import bot4cj.math.*
const ADDR_0: UInt8 = 0x68 /* 默认地址 */
const ADDR_1: UInt8 = 0x68 /* 地址2 */
const PWR_MGMT_1: UInt8 = 0x6B /* 电源管理寄存器1 */
const PWR_NOSLEEP: UInt8 = 0x00 /* 电源管理 - 非睡眠模式 */
const GYRO_CONF_REG: UInt8 = 0x1B /* 陀螺仪配置寄存器 */
const GYRO_FS_500DPS: UInt8 = 0x08 /* 陀螺仪量程 - 正负500度/秒 */
const GYRO_REG_X_HIGH: UInt8 = 0x43 /* x - 陀螺仪横轴 高字节 */
const GYRO_REG_X_LOW: UInt8 = 0x44
const GYRO_REG_Y_HIGH: UInt8 = 0x45 /* y */
const GYRO_REG_Y_LOW: UInt8 = 0x46
const GYRO_REG_Z_HIGH: UInt8 = 0x47 /* z */
const GYRO_REG_Z_LOW: UInt8 = 0x48
const ACCEL_CONF_REG: UInt8 = 0x1C /* 加速度计配置寄存器 */
const ACCEL_FS_8g: UInt8 = 0x10 /* 加速度计量程 正负8g */
const ACCEL_REG_X_HIGH: UInt8 = 0x3B
const ACCEL_REG_X_LOW: UInt8 = 0x3C
const ACCEL_REG_Y_HIGH: UInt8 = 0x3D
const ACCEL_REG_Y_LOW: UInt8 = 0x3E
const ACCEL_REG_Z_HIGH: UInt8 = 0x3F
const ACCEL_REG_Z_LOW: UInt8 = 0x40
/* -------- MPU6050传感器数据 -------- */
@C
public struct MPU6050_sensorData {
public var accel_x: Int16 = 0 /* 加速度计 */
public var accel_y: Int16 = 0
public var accel_z: Int16 = 0
public var gyro_x: Int16 = 0 /* 陀螺仪 */
public var gyro_y: Int16 = 0
public var gyro_z: Int16 = 0
public func print() { /* 格式化打印 */
println("[accel x, y, z] ${accel_x}, ${accel_y}, ${accel_z}")
println("[gyro x, y, z] ${gyro_x}, ${gyro_y}, ${gyro_z}")
println("--------------------")
}
}
/* -------- 设置MPU6050 -------- */
@C
func mpu6050_setup(i2c_fd: Int32, addr: UInt8) {
unsafe {
I2cLock_lock(i2c_fd)
setI2cAddr(i2c_fd, addr);
sleep(Duration.millisecond * 1)
writeI2cReg(i2c_fd, PWR_MGMT_1, PWR_NOSLEEP); /* 退出睡眠 */
writeI2cReg(i2c_fd, GYRO_CONF_REG, GYRO_FS_500DPS); /* ±500°/s */
writeI2cReg(i2c_fd, ACCEL_CONF_REG, ACCEL_FS_8g); /* ±8g */
I2cLock_unlock(i2c_fd)
sleep(Duration.millisecond * 100); /* 等待稳定 */
}
}
@C
/* -------- 读取 16 位传感器数据(带符号)--------
* FIXME: 是否能够正确处理符号位 */
func read16BitReg(i2c_fd: Int32, high: UInt8, low: UInt8): Int16 {
unsafe {
let hh: Int16 = Int16(readI2cReg(i2c_fd, high))
let ll: Int16 = Int16(readI2cReg(i2c_fd, low))
return (hh << 8) | ll
}
}
/* -------- 读取传感器数据 -------- */
@C
func mpu6050_readSensor(i2c_fd: Int32, addr: UInt8) {
var data = MPU6050_sensorData()
unsafe {
I2cLock_lock(i2c_fd)
setI2cAddr(i2c_fd, addr)
data.accel_x = read16BitReg(i2c_fd, ACCEL_REG_X_HIGH, ACCEL_REG_X_LOW);
data.accel_y = read16BitReg(i2c_fd, ACCEL_REG_Y_HIGH, ACCEL_REG_Y_LOW);
data.accel_z = read16BitReg(i2c_fd, ACCEL_REG_Z_HIGH, ACCEL_REG_Z_LOW);
data.gyro_x = read16BitReg(i2c_fd, GYRO_REG_X_HIGH, GYRO_REG_X_LOW);
data.gyro_y = read16BitReg(i2c_fd, GYRO_REG_Y_HIGH, GYRO_REG_Y_LOW);
data.gyro_z = read16BitReg(i2c_fd, GYRO_REG_Z_HIGH, GYRO_REG_Z_LOW);
I2cLock_unlock(i2c_fd)
}
return data
}
/* -------- 计算PITCH角 -------- */
//@C
//func mpu6050_calcPitch()
/* -------- mpu6050主线程 --------
* TODO(20260525-qing): 使用互补滤波就够用了 */
const RAD_TO_DEG = 57.2956f32 /* ---- 弧度转换为角度的系数(180/π ≈ 57.2956) ---- */
//foreign func pthread_testcancel(): Unit
@C
func mpu6050_main(args: CPointer<Unit>): CPointer<Unit> {
/* -------- 获取传入线程的参数 -------- */
let param = unsafe{ CPointer<mpu6050_th_param>(args).read() }
/* -------- 为加速度计准备滤波器组 -------- */
var kalman_accel_x = extKalman(20.0f32, 200.0f32)
var kalman_accel_y = extKalman(20.0f32, 200.0f32)
var kalman_accel_z = extKalman(20.0f32, 200.0f32)
/* -------- 为陀螺仪准备滤波器组 -------- */
var kalman_gyro_x = extKalman(20.0f32, 200.0f32)
var kalman_gyro_y = extKalman(20.0f32, 200.0f32)
var kalman_gyro_z = extKalman(20.0f32, 200.0f32)
while(true) {
println("I am running. i2c_fd: ${param.i2c_fd}, addr: ${param.addr}")
//unsafe { _mutex_lock(mutex) }
/* -------- 获取传感器数据 -------- */
var data = unsafe { mpu6050_readSensor(param.i2c_fd, param.addr) }
/* -------- 为加速度计执行滤波操作 -------- */
let filtered_accel_x = kalman_accel_x.filter(Float32(data.accel_x))
let filtered_accel_y = kalman_accel_y.filter(Float32(data.accel_y))
let filtered_accel_z = kalman_accel_z.filter(Float32(data.accel_z))
/* -------- 为陀螺仪执行滤波操作 -------- */
let filtered_gyro_x = kalman_gyro_x.filter(Float32(data.gyro_x))
let filtered_gyro_y = kalman_gyro_y.filter(Float32(data.gyro_y))
let filtered_gyro_z = kalman_gyro_z.filter(Float32(data.gyro_z))
/* -------- 计算Pitch(俯仰角)和Roll(翻滚角) -------- */
let roll = atan2(filtered_accel_y, filtered_accel_z) * RAD_TO_DEG
let pitch = atan(-filtered_accel_x / sqrt(filtered_accel_y * filtered_accel_y + filtered_accel_z * filtered_accel_z)) * RAD_TO_DEG
let roll1 = atan(filtered_accel_y / sqrt(filtered_accel_x * filtered_accel_x + filtered_accel_z * filtered_accel_z)) * RAD_TO_DEG
let pitch1 = atan2(-filtered_accel_x, filtered_accel_z) * RAD_TO_DEG
print("roll: ${roll}\t")
println("pitch: ${pitch}")
print("roll1: ${roll1}\t")
println("pitch1: ${pitch1}")
//unsafe { _mutex_unlock(mutex) }
sleep(Duration.millisecond * 10)
}
/* -------- 返回NULL指针以占位 -------- */
return CPointer<Unit>()
}
/* -------- mpu6050线程参数 --------
* INFO(20260522-qing): mutex字段是一个锁的指针,需要在MPU6050的构造函数中获取资源
* - 用于辅助别的线程获取值。 */
@C
struct mpu6050_th_param {
public var i2c_fd: Int32 = 0 /* i2c文件句柄 */
public var addr: UInt8 = 0 /* i2c设备地址 */
public var mutex = CPointer<UInt8>() /* 用于辅助别的线程获取值的锁 */
}
/* -------- MPU6050陀螺仪、加速度计 --------
* - 输入并持有一个I2C文件句柄 */
public class MPU6050 {
/* -------- 主初始化器 --------
* FIXME(20260517-qing): 由于gc的原因,我们可能会手动构造RAII,
* - 将一部分初始化移动到别的函数 */
public init(i2c_fd: Int32, addr: UInt8) {
this.i2c_fd = i2c_fd
this.addr = addr
unsafe {
param = LibC.malloc<mpu6050_th_param>()
var tmp = mpu6050_th_param()
tmp.i2c_fd = i2c_fd
tmp.addr = addr
tmp.mutex = LibC.malloc<UInt8>()
param.write(tmp)
}
}
/* -------- 终结器 -------- */
~init() {
/* -------- 清除构造时申请的线程参数和锁 -------- */
unsafe {
var tmp = param.read()
LibC.free(tmp.mutex)
LibC.free(param)
}
}
/* -------- 初始化该模块 -------- */
public func setup() {
unsafe { mpu6050_setup(i2c_fd, addr) }
}
/*----------------*/
private var i2c_fd: Int32 /* -------- I2C设备文件句柄 -------- */
private var addr: UInt8 /* -------- I2C设备地址 -------- */
/* -------- 获取原始MPU6050传感器数据 -------- */
public func readSensor(): MPU6050_sensorData {
return unsafe { mpu6050_readSensor(i2c_fd, addr) }
}
/* -------- mpu6050启动守护 --------
* TODO(20260520-qing): 放进类中
* --------------------------------- */
var param: CPointer<mpu6050_th_param> /* -------- 线程参数必须能够持久保存 -------- */
var pth: Option<Pth> = None /* -------- 线程对象 -------- */
public func startDaemon() { /* -------- 启动线程 -------- */
pth = Pth(mpu6050_main, CPointer<Unit>(param))
}
/* -------- 停止线程 -------- */
public func stopDaemon() {
if (let Some(P) <- pth) {
P.close() }
}
/* -------- 等待线程终止 -------- */
public func waitDaemon() {
if (let Some(P) <- pth) {
var ptr: CPointer<Unit> = CPointer<Unit>()
P.join(CPointer<CPointer<Unit>>(CPointer<CPointer<Unit>>(ptr))) }
}
}
+69
View File
@@ -0,0 +1,69 @@
package bot4cj.hd
import pthread4cj.*
/* pca9685舵机控制版 - bot4cj
* TODO(20260515-qing): 由于对统一接口的需求,设计基于回调的虚拟化硬件
* TODO(20260516-qing): 获取pca9685的中文或者英文参考资料
* NOTE(20260618-qing): 该代码已经被Lua脚本所取代,留着备用说不定以后会删除。 */
public class PCA9685 {
private static const ADDR_0: UInt8 = 0x40 /* PCA9685的i2c地址 */
private let i2c_fd: Int32
private let addr: UInt8
public init( i2c_fd: Int32,
addr!: UInt8 = ADDR_0) { /* PCA9685硬件地址 */
this.i2c_fd = i2c_fd
this.addr = addr
/* -------- 初始化PCA9685 --------
* FIXME(20260517-qing): 在旧版机器人代码移除以后,移除这一段 */
}
/* -------- 设置PWM频率为50Hz(舵机标准周期20ms0) --------
* TODO(20260516-qing): 不将数值写死 */
public func setup() {
unsafe {
I2cLock_lock(i2c_fd)
setI2cAddr(i2c_fd, addr);
writeI2cReg(i2c_fd, 0x00, 0x20); /* 开启AI(地址自增)和关闭SLEEP */
sleep(5000 * Duration.nanosecond); /* 等待振荡器启动 */
/* FIXME: 这一条是不是重复了 */
var freq: Float32 = 50.0;
var prescale_val: Float32 = 25000000.0 / (4096.0 * freq) - 1.0; /* 25MHz主频 */
var prescale: UInt8 = UInt8(prescale_val + 0.5);
writeI2cReg(i2c_fd, 0x00, 0x10); /* 进入SLEEP模式(允许改频率) */
writeI2cReg(i2c_fd, 0xFE, prescale); /* 写入预分频值 */
writeI2cReg(i2c_fd, 0x00, 0x20); /* 退出SLEEP */
I2cLock_unlock(i2c_fd)
}
sleep(5000 * Duration.nanosecond); /* 等待振荡器启动 */
}
/* -------- 设置指定通道的PWM信号(0-4095)-------- */
public func setPWM(channel: UInt8, on: UInt16, off: UInt16) {
var reg: UInt8 = 0x06 + 4 * channel; /* LED0 寄存器起始地址 */
unsafe {
writeI2cReg(i2c_fd, reg, UInt8(on & 0xFF));
writeI2cReg(i2c_fd, reg + 1, UInt8(on >> 8));
writeI2cReg(i2c_fd, reg + 2, UInt8(off & 0xFF));
writeI2cReg(i2c_fd, reg + 3, UInt8(off >> 8));
}
return
}
/* -------- 角度转PWM值(0°~180° → 0.5ms~2.5ms脉宽) -------- */
public func setAngle(channel: UInt8, angle: Float32) {
const min: Float32 = 0.5; /* 0°对应0.5ms */
const max: Float32 = 2.5; /* 180°对应2.5ms */
let width: Float32 = min + (max - min) * (angle / 180.0);
let off_val: UInt16 = UInt16(width * 4096.0 / 20.0); /* 20ms 周期 */
unsafe {
I2cLock_lock(i2c_fd)
setI2cAddr(i2c_fd, this.addr);
this.setPWM(channel, 0, off_val); /* ON=0, OFF=计算值 */
I2cLock_unlock(i2c_fd)
}
}
}
+30
View File
@@ -0,0 +1,30 @@
package bot4cj.hd
import std.math.*
/* -------- 舵机类 --------
* 含最大值、最小值、漂移值三个调整项
* 以及一个设置角度回调函数
*
* FIXME(20260618-qing): 在构造函数中设置角度最大值 */
public class Servo {
private var max: Float32
private var min: Float32
private var offset: Float32
private var callback_setAngle: (Float32) -> Unit
private static let MAX_ANGLE: Float32 = 180.0f32
public init(max!: Float32, min!: Float32, offset!: Float32,
callback_setAngle!: (Float32) -> Unit) {
this.max =max
this.min = min
this.offset = offset
this.callback_setAngle = callback_setAngle
}
public func setAngle(angle: Float32) {
var a = angle
a += offset /* ---- 加上漂移值 ---- */
if (a > abs(max)) { a = abs(max)}
if (a < abs(min)) { a = abs(min)}
if (max < 0.0f32) { a = MAX_ANGLE - a }
callback_setAngle(a)
}
}
+16
View File
@@ -0,0 +1,16 @@
macro package bot4cj.hd.extern
import std.fs.*
import std.io.*
import std.ast.*
public macro ExternHardware(attrTokens: Tokens, inputTokens: Tokens): Tokens {
let expr = LitConstExpr(attrTokens)
let file = File(expr.literal.value, OpenMode.Read)
let bytes = readToEnd(file)
file.close()
let sb = StringBuilder()
sb.appendFromUtf8(bytes)
let code = sb.toString()
return cangjieLex(code)
}
+138
View File
@@ -0,0 +1,138 @@
package bot4cj.hd
import ctype4cj.*
const FBIOGET_VSCREENINFO = 0x4602u64
const FBIOGET_FSCREENINFO = 0x4600u64
const PROT_WRITE: C_Int = 0x2
const MAP_SHARED: C_Int = 0x01
/* --- 定义在I2C.cj里了 ---*/
//foreign func ioctl(fd: C_Int, request: C_ULong, ...): C_Int
/* ---- 内存映射 ---- */
public type off_t = C_LongLong
foreign func mmap(addr: CPointer<Unit>, length: UIntNative, prot: C_Int, flags: C_Int, fd: C_Int, offset: off_t): CPointer<Unit>
/* -------- 打开framebuffer帧缓冲设备 -------- */
@C
public func fb_open(fb_dev: CString): C_Int {
return unsafe { libc_open(fb_dev, O_RDWR) }
}
/* -------- 帧缓冲区固定参数 -------- */
@C
public struct fb_fix_screeninfo {
var id: VArray<C_Char, $16> /* 设备驱动名称字符串,比如"TT Builtin" */
var smem_start: C_ULong = 0 /* 帧缓冲内存(显存)的物理起始地址 */
/* (physical address) */
var smem_len: UInt32 = 0 /* 帧缓冲内存的总长度,单位字节 */
var type_: UInt32 = 0 /* 显存类型(fb存储布局)see FB_TYPE_* */
var type_aux: UInt32 = 0 /* 显存附加类型 */
var visual: UInt32 = 0 /* 色彩显示模式 see FB_VISUAL_* */
var xpanstep: UInt16 = 0 /* zero if no hardware panning */
var ypanstep: UInt16 = 0 /* zero if no hardware panning */
var ywrapstep: UInt16 = 0 /* zero if no hardware ywrap */
var line_length: UInt32 = 0 /* 显示屏上一行像素数据所占用的字节数 */
var mmio_start: C_ULong = 0 /* 内存映射I/O的物理起始地址 */
/* (physical address) */
var mmio_len: UInt32 = 0 /* 内存映射的区域长度,单位字节 */
var accel: UInt32 = 0 /* 硬件加速类型 */
/* specific chip/card we have */
var capabilities: UInt16 = 0 /* see FB_CAP_* */
var reserved: VArray<UInt16, $2> /* 保留字段,用于未来扩展,确保兼容 */
public init() {
id = VArray<C_Char, $16> (repeat: 0)
reserved = VArray<UInt16, $2> (repeat: 0)
}
}
/* 颜色字段偏移量的解释:所有偏移量均从右侧开始计算,
* 位于一个“像素”值内部,该像素值的宽度正好等于 'bits_per_pixel'
* (也就是说:你可以将偏移量作为 << 的右操作数使用)。
* 之后的像素就是一个比特流,会原封不动地写入显存。
*
* 对于伪彩色:所有颜色分量的偏移量和长度应该相同。
* 偏移量指定了调色板索引的最低有效位在像素值中的位置。
* 长度表示可用的调色板条目数量(即条目数 = 1 << 长度)。
*/
@C
public struct fb_bitfield {
var offset: UInt32 = 0 /* 位域的起始位置(从右侧算起)*/
var length: UInt32 = 0 /* 位域的长度(比特数) */
var msb_right: UInt32 = 0 /* != 0 : 最高有效位在右侧 */
/* (即低位在右) */
}
/* -------- 帧缓冲区可变参数 -------- */
@C
public struct fb_var_screeninfo {
var xres: UInt32 = 0 /* visible resolution */
var yres: UInt32 = 0
var xres_virtual: UInt32 = 0 /* virtual resolution */
var yres_virtual: UInt32 = 0
var xoffset: UInt32 = 0 /* offset from virtual to visible */
var yoffset: UInt32 = 0 /* resolution */
var bits_per_pixel: UInt32 = 0 /* guess what */
var grayscale: UInt32 = 0 /* 0 = color, 1 = grayscale, */
/* >1 = FOURCC */
var red: fb_bitfield /* bitfield in fb mem if true color, */
var green: fb_bitfield /* else only length is significant */
var blue: fb_bitfield
var transp: fb_bitfield /* transparency */
var nonstd: UInt32 = 0 /* != 0 Non standard pixel format */
var activate: UInt32 = 0 /* see FB_ACTIVATE_* */
var height: UInt32 = 0 /* height of picture in mm */
var width: UInt32 = 0 /* width of picture in mm */
var accel_flags: UInt32 = 0 /* (OBSOLETE) see fb_info.flags */
/* Timing: All values in pixclocks, except pixclock (of course) */
var pixclock: UInt32 = 0 /* pixel clock in ps (pico seconds) */
var left_margin: UInt32 = 0 /* time from sync to picture */
var right_margin: UInt32 = 0 /* time from picture to sync */
var upper_margin: UInt32 = 0 /* time from sync to picture */
var lower_margin: UInt32 = 0
var hsync_len: UInt32 = 0 /* length of horizontal sync */
var vsync_len: UInt32 = 0 /* length of vertical sync */
var sync: UInt32 =0 /* see FB_SYNC_* */
var vmode: UInt32 = 0 /* see FB_VMODE_* */
var rotate: UInt32 = 0 /* angle we rotate counter clockwise */
var colorspace: UInt32 = 0 /* colorspace for FOURCC-based modes */
var reserved: VArray<UInt32, $4>/* Reserved for future compatibility */
public init() {
red = fb_bitfield()
green = fb_bitfield()
blue = fb_bitfield()
transp = fb_bitfield()
reserved = VArray<UInt32, $4> (repeat: 0)
}
}
/* ---- 获取帧缓冲区可变参数 ---- */
@C
public func fb_get_var_info(fd: C_Int) {
var info = fb_var_screeninfo()
unsafe { ioctl(fd, FBIOGET_VSCREENINFO, inout info) }
return info
}
/* ---- 获取帧缓冲区固定参数 ---- */
@C
public func fb_get_fix_info(fd: C_Int) {
var info = fb_fix_screeninfo()
unsafe { ioctl(fd, FBIOGET_FSCREENINFO, inout info) }
return info
}
/* ---- 内存映射 ---- */
@C
public func fb_mmap(addr: CPointer<Unit>, length: UIntNative, fd: C_Int, offset: off_t): CPointer<Unit>
{
let buf = unsafe { mmap(addr, length, PROT_WRITE, MAP_SHARED, fd, offset) }
return buf
}
+22
View File
@@ -0,0 +1,22 @@
package bot4cj.hd
import std.time.*
//foreign func play_frequency(frequency: Int32, duration_ms: Int32, sample_rate: Int32): Bool
//foreign func play_frequency(frequency: Float32, duration_ms: Int32, volume: Float64, channels: Int32, sample_rate: Int32): Bool
/* -------- 扬声器类 --------
* TODO: 一次打开设备,一段时间后再关闭 */
public class Spk {
private var channels: Int32
private var sample_rate: Int32
public init(channels!: Int32, sample_rate!: Int32) {
this.channels = channels
this.sample_rate = sample_rate
}
/* -------- 控制扬声器发声 -------- */
public func play(frequency: Float32, volume: Float64, durations_ms: Int32): Bool {
//unsafe { play_frequency(frequency, durations_ms, volume, channels, sample_rate) }
sleep(Duration.millisecond * Int64(durations_ms))
return true
}
}
+177
View File
@@ -0,0 +1,177 @@
package bot4cj
import std.io.*
import std.fs.*
import std.env.*
import std.time.*
import std.argopt.*
import std.convert.*
import std.collection.*
import stdx.encoding.json.*
import bot4cj.hd.*
import bot4cj.math.*
import ctype4cj.*
/* ---- 神经网络输出路径 ---- */
var NNS_OUT: Option<String> = None
main(args: Array<String>): Int64 {
/* ---- 解析命令行参数 ---- */
let specs = [
ArgumentSpec.Long("help", ArgumentMode.NoValue), /* ---- 拉起帮助菜单 ---- */
ArgumentSpec.Short(r'i', ArgumentMode.RequiredValue), /* ---- 神经网络模型 - 输入 ---- */
ArgumentSpec.Short(r'o', ArgumentMode.RequiredValue), /* ---- 神经网络模型 - 输出 ---- */
ArgumentSpec.Long("test", ArgumentMode.RequiredValue) /* ---- 运行一个测试分支 - 需要输入测试类型 ---- */
]
/* ---- 执行解析 ---- */
let parsed = parseArguments(args, specs)
let options = parsed.options
//---------------------------
/* 读取设置项 */
/*---------------------------*/
/*---------------------------*/
/* 初始化神经网络 (init nn)
* TODO: 可以去掉内部初始化参数,使用矩阵式模板进行初始化 */
//var nns: ArrayList<Nn> =
//if (options.keys().contains("i")) {
// let filename = options['i']
// load_nns(filename) }
//else {
// let nn_in = ArrayList<Nn>()
// nn_in.add(Nn(41, 80, ActivationFuncs.Leaky_ReLU, 0.2f32))
// nn_in.add(Nn(80, 80, ActivationFuncs.Leaky_ReLU, 0.2f32))
// nn_in.add(Nn(80, 40, ActivationFuncs.Leaky_ReLU, 0.2f32))
// nn_in.add(Nn(40, 17, ActivationFuncs.Sigmoid, 0.2f32)) // 16个舵机加1个时延
// nn_in }
/* ---- 使用-o选项进行输出路径的选择 ---- */
if (options.contains("o")) {
NNS_OUT = Option<String>.Some(options['o'])
}
/*---------------------------*/
/*---------------------------*/
/* 分支选择 */
/* -------- 拉起帮助手册
* -------- */
if (options.contains("help")) {
print_help()
return 0
}
/* -------- 启动一个测试分支
* -------- */
else if (options.contains("test")) {
run_test(options["test"])
return 0
}
/* -------- 缺省行为:从标准输入获取动作脚本
* --------
* -------- TODO(20260519-qing): 进行脚本引入(相对路径也可,从**环境变量**的路径中搜寻也可) */
else {
let consoleReader = getStdIn()
let robot = RobotStruct(consoleReader)
try {
robot.active()
robot.run()
}
finally {
robot.deactive()
}
/* ---- NOTE(20260523-qing): 储存微调网络模型
* ---- */
// let consoleReader = getStdIn()
// run_action_drl(consoleReader, nns)
// if (options.contains("o")) {
// save_nns(nns, options['o']) }
}
return 0
}
/* -------- 打印帮助手册
* -------- */
func print_help()
{
println("这是一个机器人动作脚本解析器。")
println("使用管道输入动作脚本,示例:")
println("cat std.mt | ./bot")
println()
println("--i2c=/path/to/device\t选择i2c设备")
println()
println("-i 选择输入的模型的路径")
println("-o 设置输出的模型的路径")
}
/* -------- 集成多种常规测试、临时测试
* -------- */
func run_test(key: String) {
/* ----------- 神经网络与门测试
* -----------
* ----------- INFO: 我们使用随机生成的动态数据集,这样会导致一些后果 */
if (key == "nn") {
let nn_t = ArrayList<Nn1>()
nn_t.add(Nn1(2, 4, 0.05f32, Af.Leaky_ReLU(FP32(0.01))))
nn_t.add(Nn1(4, 1, 0.05f32, Af.Sigmoid))
let batch = 1
let num =2000
let prt_num = 100
for (i in 0..(num*batch)) {
let m = Matrix1(1, 2)
let t = Matrix1(1, 1)
prepare_andDoor(m, t)
let l = train1(nn_t, m, t, 1.0f32) // 这个1.0是学习率的折扣,而非学习率本身
if (i % prt_num == prt_num-1) {
print("${i}\t:")
print("${l}")
println()
}
}
}
else if (key == "cnn") {
testCNN()
}
else if (key == "cnn1") {
testGeometryCNN()
}
/* -------- 测试解析矩阵,并且输出
* -------- */
else if (key == "matrix_o") {
let file = File("ss/test.mt", OpenMode.Read)
let robot = RobotStruct(file)
file.close()
let outf = File("ss/output.mt", OpenMode.Write)
robot.save(outf)
outf.close()
}
/* -------- 解析测试输出的矩阵
* -------- */
else if (key == "matrix_2") {
let file = File("ss/output.mt", OpenMode.Read)
let robot = RobotStruct(file)
file.close()
robot.active()
robot.run()
robot.deactive()
let outf = File("ss/output_1.mt", OpenMode.Write)
robot.save(outf)
outf.close()
}
}
+204
View File
@@ -0,0 +1,204 @@
// Adam.cj - Adam 优化器实现(添加全连接层支持)
package bot4cj.math
import std.math.*
import std.collection.*
public class Adam {
let learningRate: FP32
let beta1: FP32
let beta2: FP32
let epsilon: FP32
var m: HashMap<String, Matrix1>
var v: HashMap<String, Matrix1>
var t: Int64
// 完整参数构造函数
public init(
lr: FP32,
beta1: FP32,
beta2: FP32,
epsilon: FP32
) {
learningRate = lr
this.beta1 = beta1
this.beta2 = beta2
this.epsilon = epsilon
m = HashMap<String, Matrix1>()
v = HashMap<String, Matrix1>()
t = 0
}
// 默认参数构造函数
public init() {
learningRate = FP32(0.001)
beta1 = FP32(0.9)
beta2 = FP32(0.999)
epsilon = FP32(0.00000001)
m = HashMap<String, Matrix1>()
v = HashMap<String, Matrix1>()
t = 0
}
// 仅自定义学习率的构造函数
public init(lr: FP32) {
learningRate = lr
beta1 = FP32(0.9)
beta2 = FP32(0.999)
epsilon = FP32(0.00000001)
m = HashMap<String, Matrix1>()
v = HashMap<String, Matrix1>()
t = 0
}
/**
* 计算幂运算的辅助函数
*/
private func power(base: FP32, exp: Int64): FP32 {
var result = FP32(1.0)
for (i in 0..exp) {
result = result * base
}
return result
}
/**
* 计算平方根的辅助函数 - 使用牛顿迭代法
*/
private func sqrt(val: FP32): FP32 {
let x = val.getValue()
if (x <= 0.0) {
return FP32(0.0)
}
var guess = x
for (i in 0..10) {
guess = (guess + x / guess) / 2.0
}
return FP32(guess)
}
/**
* 更新矩阵参数(通用方法)
* @param param 要更新的参数矩阵
* @param grad 梯度矩阵
* @param key 参数在HashMap中的键名
*/
private func updateMatrix(
param: Matrix1,
grad: Matrix1,
key: String
): Unit {
// 初始化动量
let hasState = m.contains(key)
if (!hasState) {
let rows = param.get_row()
let cols = param.get_col()
m.add(key, Matrix1(rows, cols))
v.add(key, Matrix1(rows, cols))
}
let mMat = m.get(key).getOrThrow()
let vMat = v.get(key).getOrThrow()
let rows = param.get_row()
let cols = param.get_col()
for (r in 0..rows) {
for (c in 0..cols) {
let g = grad.get(r, c).getOrThrow()
// 更新一阶矩
let oldM = mMat.get(r, c).getOrThrow()
let newM = beta1 * oldM + (FP32(1.0) - beta1) * g
mMat.set(r, c, newM)
// 更新二阶矩
let oldV = vMat.get(r, c).getOrThrow()
let newV = beta2 * oldV + (FP32(1.0) - beta2) * g * g
vMat.set(r, c, newV)
// 偏差修正
let one = FP32(1.0)
let beta1Pow = power(beta1, t)
let beta2Pow = power(beta2, t)
let mHat = newM / (one - beta1Pow)
let vHat = newV / (one - beta2Pow)
// 更新参数
let oldParam = param.get(r, c).getOrThrow()
let update = learningRate * mHat / (sqrt(vHat) + epsilon)
param.set(r, c, oldParam - update)
}
}
}
/**
* 更新卷积层参数
*/
public func updateConvLayer(layer: ConvLayer): Unit {
t += 1
// 更新卷积核
var kGrad = match (layer.kernelsGrad) {
case Some(val) => val
case _ => throw Exception("No kernel gradients")
}
for (oc in 0..layer.nOutputChannels) {
let key = "conv_kernel_" + oc.toString()
updateMatrix(layer.kernels[oc], kGrad[oc], key)
}
// 更新偏置
var bGrad = match (layer.biasGrad) {
case Some(val) => val
case _ => throw Exception("No bias gradient")
}
let biasKey = "conv_bias"
updateMatrix(layer.biases, bGrad, biasKey)
}
/**
* 更新全连接层参数(Nn1)
*/
public func updateFCLayer(layer: Nn1): Unit {
t += 1
// 更新权重
var wGrad = match (layer.weights_grad) {
case Some(val) => val
case _ => throw Exception("No weights gradient")
}
let weightKey = "fc_weights"
updateMatrix(layer.weights, wGrad, weightKey)
// 更新偏置
var bGrad = match (layer.bias_grad) {
case Some(val) => val
case _ => throw Exception("No bias gradient")
}
let biasKey = "fc_bias"
updateMatrix(layer.bias, bGrad, biasKey)
}
/**
* 重置优化器状态
*/
public func reset(): Unit {
m.clear()
v.clear()
t = 0
}
/**
* 获取当前迭代次数
*/
public func getStep(): Int64 {
t
}
}
+273
View File
@@ -0,0 +1,273 @@
package bot4cj.math
import std.math.*
/* -------- Activation Functions 激活函数族 --------
* TODO(20260516-qing): 增加序列化、反序列化工厂函数 */
public enum Af {
None | Sigmoid | ReLU | Leaky_ReLU(FP32) | Tanh | Softmax
/* -------- 执行激活函数 -------- */
public func forward(x: Matrix1) {
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)
case Softmax => softmax(x)
}
}
/* -------- 激活函数的导数 -------- */
public func backward(y: Matrix1) {
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)
case Softmax => softmax_jacobian(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"
case Softmax => "Softmax"
}
}
/* */
public func get_value(): FP32 {
return match(this) {
case None => FP32(0.0f32)
case Sigmoid => FP32(0.0f32)
case ReLU => FP32(0.0f32)
case Leaky_ReLU(alpha) => alpha
case Tanh => FP32(0.0f32)
case Softmax => FP32(0.0f32)
}
}
/* -------- 空的激活函数 -------- */
private static func none(M: Matrix1) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x = M.get(r, c).getOrThrow()
let x1= x.getValue()
N.set(r, c, FP32(x1)) }
}
return N
}
/* -------- 空激活函数的导数 -------- */
private static func none_dot(M: Matrix1) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
for (r in 0..row) {
for (c in 0..col) {
N.set(r, c, FP32(1.0f32)) }
}
return N
}
/* -------- S型生长曲线 -------- */
private static func sigmoid(M: Matrix1) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x = M.get(r, c).getOrThrow()
let x1= x.getValue()
let x2 = 1.0f32 / (1.0f32 + exp(-x1))
N.set(r, c, FP32(x2)) }
}
return N
}
/* -------- S型生长曲线的导数 -------- */
private static func sigmoid_dot(M: Matrix1) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x = M.get(r, c).getOrThrow()
let x1= x.getValue()
let x2 = 1.0f32 / (1.0f32 + exp(-x1))
let x3 = x2 * (1.0f32 - x2)
N.set(r, c, FP32(x3)) }
}
return N
}
/* -------- 线性整流单元 -------- */
private static func relu(M: Matrix1) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x = M.get(r, c).getOrThrow()
let x1= x.getValue()
let x2 =if (x1 > 0.0f32) {x1} else{0.0f32}
N.set(r, c, FP32(x2))
}
}
return N
}
/* -------- 线性整流单元的导数 -------- */
private static func relu_dot(M: Matrix1) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x = M.get(r, c).getOrThrow()
let x1= x.getValue()
let x2 =if (x1 > 0.0f32) {1.0f32} else{0.0f32}
N.set(r, c, FP32(x2))
}
}
return N
}
/* -------- 带泄露的线性整流单元 -------- */
private static func leaky_relu(M: Matrix1, alpha: FP32) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x = M.get(r, c).getOrThrow()
let x1= x.getValue()
let x2 =if (x1 > 0.0f32) {x1} else{x1 * alpha.getValue()}
N.set(r, c, FP32(x2))
}
}
return N
}
/* -------- 带泄露的整流单元导数 -------- */
private static func leaky_relu_dot(M: Matrix1, alpha: FP32) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x = M.get(r, c).getOrThrow()
let x1= x.getValue()
let x2 =if (x1 > 0.0f32) { 1.0f32 } else{alpha.getValue()}
N.set(r, c, FP32(x2)) }
}
return N
}
/* -------- 双曲正切函数 --------*/
private static func tanh(M: Matrix1) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
for (r in 0..row) {
for (c in 0..col) {
let x = M.get(r, c).getOrThrow()
let x1= x.getValue()
let x2 = tanh(x1)
N.set(r, c, FP32(x2)) }
}
return N
}
/* -------- 双曲正切函数的导数 -------- */
private static func tanh_dot(M: Matrix1) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
for (r in 0..row) {
for (c in 0..col) {
let y = M.get(r, c).getOrThrow()
let y_val= y.getValue() /*---- NOTE(20260525-qing): 修复命名(x => y) ----*/
let rea = 1.0f32 - y_val * y_val
N.set(r, c, FP32(rea)) }
}
return N
}
/* -------- 归一化指数函数 -------- */
private static func softmax(M: Matrix1) {
let row = M.get_row()
let col = M.get_col()
let N = Matrix1(row, col)
var max_val = 0.0f32
for (r in 0..row) {
for (c in 0..col) {
let x = M.get(r, c).getOrThrow()
let x1= x.getValue()
if (x1 > max_val) {
max_val = x1 }
}
}
var sum = 0.0f32
for (r in 0..row) {
for (c in 0..col) {
let x = M.get(r, c).getOrThrow()
let x1= x.getValue()
let x2 = exp(x1 - max_val)
N.set(r, c, FP32(x2))
sum += x2
}
}
for (r in 0..row) {
for (c in 0..col) {
let x = N.get(r, c).getOrThrow()
let x1= x.getValue()
let x2 = x1 / sum
N.set(r, c, FP32(x2))
}
}
return N
}
/* -------- 计算Softmax的雅可比矩阵 --------
* 输入:已计算好的softmax输出(概率分布)
* 输出:雅可比矩阵 J[i][j] = ∂y_i/∂x_j */
private func softmax_jacobian(softmax_output: Matrix1) {
let row = softmax_output.get_row()
let col = softmax_output.get_col()
let n = row * col
var jacobian = Matrix1(n, n)
for (i in 0..n) {
for (j in 0..n) {
let row_i = i / col /* 需要将一维索引转换为二维索引来访问Matrix1 */
let col_i = i % col
let row_j = j / col
let col_j = j % col
let val_i = softmax_output.get(row_i, col_i).getOrThrow().getValue()
let val_j = softmax_output.get(row_j, col_j).getOrThrow().getValue()
if (i == j) { /* 对角线元素: y_i * (1 - y_i) */
jacobian.set(i, j, FP32(val_i * (1.0f32 - val_i))) }
else { /* 非对角线元素: -y_i * y_j */
jacobian.set(i, j, FP32(-val_i * val_j)) }
}
}
return jacobian
}
}
+37
View File
@@ -0,0 +1,37 @@
package bot4cj.math
import std.random.*
import std.time.*
import std.math.*
/* -------- 神经网络使用的权重矩阵初始化类 --------
* TODO(20260517-qing): 将该函数改为枚举类,统一一个接口,根据模式匹配执行初始化
* - 这样在矩阵式中可以定义初始化方式 */
public class Ini {
/* -------- xavier正态初始化 -------- */
public static func xavierNormalInit(target: Matrix1, fan_in: Int64, fan_out: Int64) {
let seed = DateTime.now().nanosecond
let random = Random(UInt64(seed))
let sigma = sqrt(2.0f32 / Float32(fan_in + fan_out)) /* 计算标准差 */
for (r in 0..fan_in) {
for (c in 0..fan_out) {
let v = random.nextGaussianFloat32(mean: 0.0f32, sigma:sigma)
target.set(r, c, FP32(v))
}
}
}
/* -------- Glorot均匀初始化 -------- */
public static func xavierUniformInit(target: Matrix1, fan_in: Int64, fan_out: Int64) {
let limit = sqrt(6.0f32 / Float32(fan_in + fan_out))
let seed = DateTime.now().nanosecond
let random = Random(UInt64(seed))
for (r in 0..fan_in) {
for (c in 0..fan_out) {
let v = (random.nextFloat32() * 2.0f32 - 1.0f32) * limit /* 均匀分布 [-limit, limit] */
target.set(r, c, FP32(v)) }
}
}
}
+535
View File
@@ -0,0 +1,535 @@
package bot4cj.math
import std.fs.*
import std.io.*
import std.convert.*
//foreign func createMatrix(row: UInt64, col: UInt64): CPointer<Unit>
//foreign func freeMatrix(M: CPointer<Unit>): Unit
//foreign func witchMatrix(M: CPointer<Unit>, row: UInt64, col: UInt64): CPointer<Float32>
//foreign func getMatrix(M: CPointer<Unit>, row: UInt64, col: UInt64): Float32
//foreign func setMatrix(M: CPointer<Unit>, row: UInt64, col: UInt64, v: Float32): Unit
//foreign func getrowMatrix(M: CPointer<Unit>): UInt64
//foreign func getcolMatrix(M: CPointer<Unit>): UInt64
//foreign func sameShapeMatrix(M: CPointer<Unit>, S: CPointer<Unit>): Bool
//foreign func dotShapeMatrix(M: CPointer<Unit>, S: CPointer<Unit>): Bool
//foreign func plusMatrix(M: CPointer<Unit>, S:CPointer<Unit>): CPointer<Unit>
//foreign func plusBroadcastMatrix(M: CPointer<Unit>, T: Float32): CPointer<Unit>
//foreign func subMatrix(M: CPointer<Unit>, S: CPointer<Unit>): CPointer<Unit>
//foreign func subBroadcastMatrix(M: CPointer<Unit>, T: Float32): CPointer<Unit>
//foreign func multiMatrix(M: CPointer<Unit>, S: CPointer<Unit>): CPointer<Unit>
//foreign func multiBroadcastMatrix(M: CPointer<Unit>, T: Float32): CPointer<Unit>
//foreign func divMatrix(M: CPointer<Unit>, S: CPointer<Unit>): CPointer<Unit>
//foreign func divBroadcastMatrix(M: CPointer<Unit>, T: Float32): CPointer<Unit>
//foreign func dotMatrix(M: CPointer<Unit>, S: CPointer<Unit>): CPointer<Unit>
//foreign func transposeMatrix(M: CPointer<Unit>): CPointer<Unit>
//foreign func xavierNormalInit(fan_in: UInt64, fan_out: UInt64): CPointer<Unit>
//foreign func xavierUniformInit(fan_in: UInt64, fan_out: UInt64): CPointer<Unit>
/* -------- 32位浮点数 -------- */
public struct FP32 {
private var val: Float32
public init(t: Float32) { /* 以Float32初始化对象 */
val = t }
public func getValue() { /* 获取值 */
return val }
/* -------- 运算符重载 -------- */
//public operator func =(t: Float32) { /*-------- this can not --------*/
// val = t }
public operator func +(t: Float32) {
return FP32(val + t)}
public operator func +(S: FP32) {
return this + S.getValue() }
public operator func -(t: Float32) {
return FP32(val - t) }
public operator func -(S: FP32) {
return this - S.getValue() }
public operator func *(t: Float32) {
return FP32(val * t) }
public operator func *(S: FP32) {
return this * S.getValue() }
public operator func /(t: Float32) {
return FP32(val / t) }
public operator func /(S: FP32) {
return this / S.getValue() }
}
/*
* 新版的矩阵对象,仓颉原生实现
* TODO(20260517-qing): 设计原生的多维数组类
*
___col___
/ \
+---------+ \
| + + + + | | row - 第一维度
| + + + + | |
+---------+ /
* TODO(20260523-qing): 以某种方式统一矩阵式的字符串矩阵和这里的数值矩阵
*/
public class Matrix1 {
private var row: Int64 /* ---- 行数 ---- */
private var col: Int64 /* ---- 列数 ---- */
private var arr: Array<FP32> /* ---- 数据 ---- */
/* -------- 构造器,默认值为0 -------- */
public Matrix1(row: Int64, col: Int64) {
this.row = row
this.col = col
this.arr = Array<FP32>(row * col, {_ => FP32(0.0f32)})
}
/* --------- 这几个方法是访问器 -------- */
/* ------------------------------------- */
/* -------- TODO(20260524-qing): 使用prop成员 -------- */
public func get(r: Int64, c: Int64) {
return arr.get(r * this.col + c)
}
public func set(r: Int64, c: Int64, v: FP32) {
arr[r * this.col + c] = v
}
public func get_row() { return row }
public func get_col() { return col }
/* -------- 这两个函数是比较器 -------- */
/* ------------------------------------ */
public func has_same_shape(other: Matrix1) {
let a = this.get_row() == other.get_row()
let b = this.get_col() == other.get_col()
return a && b
}
public func has_dot_shape(other: Matrix1) {
return this.get_col() == other.get_row()
}
/* -------- 这些是运算符重载 -------- */
/* ---------------------------------- */
public operator func +(a: FP32) { /* ---- 矩阵广播加法 ---- */
let r =get_row()
let c = get_col()
let M = Matrix1(r, c)
for (ir in 0..r) {
for (ic in 0..c) {
let v = this.get(ir, ic).getOrThrow()
M.set(ir, ic, v + a)
}
}
return M
}
public operator func +(S: Matrix1) { /* ---- 矩阵加法 ---- */
if (!has_same_shape(S)) {
throw IllegalArgumentException("Matrixes doesn't match")
}
let r =get_row()
let c = get_col()
let M = Matrix1(r, c)
for (ir in 0..r) {
for (ic in 0..c) {
let v = this.get(ir, ic).getOrThrow()
let vs = S.get(ir, ic).getOrThrow()
M.set(ir, ic, v + vs)
}
}
return M
}
public operator func -(a: FP32) { /* ---- 矩阵广播减法 ----*/
let r = get_row()
let c = get_col()
let M = Matrix1(r, c)
for (ir in 0..r) {
for (ic in 0..c){
let v = this.get(ir, ic).getOrThrow()
M.set(ir, ic, v - a)
}
}
return M
}
public operator func -(S: Matrix1) { /* ---- 矩阵减法 ---- */
if (!has_same_shape(S)) {
throw IllegalArgumentException("Matrixes doesn't match")
}
let r =get_row()
let c = get_col()
let M = Matrix1(r, c)
for (ir in 0..r) {
for (ic in 0..c) {
let v = this.get(ir, ic).getOrThrow()
let vs = S.get(ir, ic).getOrThrow()
M.set(ir, ic, v - vs)
}
}
return M
}
public operator func *(a: FP32) { /* ---- 矩阵广播乘法 ---- */
let r = get_row()
let c = get_col()
let M = Matrix1(r, c)
for (ir in 0..r) {
for (ic in 0..c){
let v = this.get(ir, ic).getOrThrow()
M.set(ir, ic, v * a)
}
}
return M
}
public operator func *(S: Matrix1) { /* ---- 逐元素相乘 ---- */
if (!has_same_shape(S)) {
throw IllegalArgumentException("Matrixes doesn't match")
}
let r =get_row()
let c = get_col()
let M = Matrix1(r, c)
for (ir in 0..r) {
for (ic in 0..c) {
let v = this.get(ir, ic).getOrThrow()
let vs = S.get(ir, ic).getOrThrow()
M.set(ir, ic, v * vs)
}
}
return M
}
public operator func /(a: FP32) { /* ---- 矩阵广播除法 ---- */
let r = get_row()
let c = get_col()
let M = Matrix1(r, c)
for (ir in 0..r) {
for (ic in 0..c){
let v = this.get(ir, ic).getOrThrow()
M.set(ir, ic, v / a)
}
}
return M
}
public operator func /(S: Matrix1) { /* ---- 逐元素除法 ---- */
if (!has_same_shape(S)) {
throw IllegalArgumentException("Matrixes doesn't match")
}
let r =get_row()
let c = get_col()
let M = Matrix1(r, c)
for (ir in 0..r) {
for (ic in 0..c) {
let v = this.get(ir, ic).getOrThrow()
let vs = S.get(ir, ic).getOrThrow()
M.set(ir, ic, v / vs)
}
}
return M
}
/* ------------------------------ */
/* -------- 矩阵点积乘法 -------- */
public func dot(S: Matrix1) {
if (!has_dot_shape(S)) {
throw IllegalArgumentException("Matrixes doesn't match")
}
let r =get_row()
let c = S.get_col()
let M = Matrix1(r, c)
for (ir in 0..get_row()) {
for (ic in 0..S.get_col()) {
var sum = FP32(0.0f32)
for (ik in 0..get_col()) {
let a = get(ir, ik).getOrThrow()
let b = S.get(ik, ic).getOrThrow()
sum += a * b
}
M.set(ir, ic, sum)
}
}
return M
}
/* -------- 克隆一个一模一样的矩阵 -------- */
public func clone() {
let r = get_row()
let c = get_col()
let N = Matrix1(r, c)
for (ir in 0..r) {
for (ic in 0..c) {
let v = get(ir, ic).getOrThrow()
N.set(ir, ic, v)
}
}
return N
}
/* -------- 矩阵转置,返回转置后的矩阵 -------- */
public func T() {
let r = get_row()
let c = get_col()
let N = Matrix1(c, r)
for (ir in 0..r) {
for (ic in 0..c) {
let v = get(ir, ic).getOrThrow()
N.set(ic, ir, v)
}
}
return N
}
}
/* -------- 旧版的矩阵类,依赖c++编译的共享动态库 --------
* TODO: 将矩阵元数据存储在仓颉部分(done at 202605xx ) */
//public class Matrix {
//
// /* -------- 主初始化器,初始化并获取资源 -------- */
// public init(row: UInt64, col: UInt64) {
// unsafe { M = createMatrix(row, col) }
// }
//
// /* -------- 初始化器,利用已经存在的元数据进行构建 -------- */
// public Matrix(m: CPointer<Unit>) { M = m }
//
// /* -------- Xavier正态分布初始化 -------- */
// public static func initWithXavierNormal(fan_in: UInt64, fan_out: UInt64) {
// unsafe {
// let m = xavierNormalInit(fan_in, fan_out);
// return Matrix(m)
// }
// }
//
// /* -------- Glorot均匀初始化 -------- */
// public static func initWithXavierUniform(fan_in: UInt64, fan_out: UInt64) {
// unsafe {
// let m = xavierUniformInit(fan_in, fan_out);
// return Matrix(m)
// }
// }
//
// /* ======== 在析构函数中释放资源 ======== */
// ~init() {
// unsafe { freeMatrix(M) }
// }
//
// /* -------- 取下标,由于不能返回值类型的引用被弃用 -------- */
// //public operator func [](r: UInt64, c: UInt64): CPointer<Float32> {
// // unsafe { return witchMatrix(M, r, c) }
// //}
//
// /* -------- 获得某个矩阵单元的值 -------- */
// public func get(r: UInt64, c: UInt64): Float32 {
// unsafe { return getMatrix(M, r, c) }
// }
//
// /* -------- 设置某个矩阵单元的值 --------*/
// public func set(r: UInt64, c: UInt64, v: Float32) {
// unsafe { setMatrix(M, r, c, v) }
// }
//
// /* ------- 两个矩阵形状是否相同 --------
// * FIXME: 这个函数所重载的运算符不贴切 */
// public operator func ==(s: Matrix): Bool {
// unsafe { return sameShapeMatrix(M, s.getData()) }
// }
//
// /* -------- 获得行数 -------- */
// public func getRow(): UInt64 {
// unsafe { return getrowMatrix(M) }
// }
//
// /* -------- 获取列数 --------*/
// public func getCol(): UInt64 {
// unsafe { return getcolMatrix(M) }
// }
//
// /* -------- 获取指向元数据的C风格指针 -------- */
// public func getData(): CPointer<Unit> { return M; }
//
// /* -------- 是否两个矩阵具有点积和的形状 -------- */
// public func hasDotShapeWith(s: Matrix): Bool {
// unsafe { return dotShapeMatrix(M, s.getData()) }
// }
//
// /* -------- 逐元素算数运算 -------- */
// /* -------------------------------- */
// public operator func +(s: Matrix): Matrix {
// unsafe {
// if (!sameShapeMatrix(M, s.getData())) {
// println('------------')
// print_shape()
// s.print_shape()
// println('------------')
// throw IllegalArgumentException("not same shape")
// }
// let r = plusMatrix(M, s.getData())
// return Matrix(r)
// }
// }
//
// public operator func +(t: Float32): Matrix {
// unsafe {
// let r = plusBroadcastMatrix(M, t)
// return Matrix(r);
// }
// }
//
// public operator func -(s: Matrix): Matrix {
// unsafe {
// if (!(this == s)) {
// println('------------')
// print_shape()
// s.print_shape()
// println('------------')
// throw IllegalArgumentException("not same shape")
// }
// let r = subMatrix(M, s.getData())
// return Matrix(r)
// }
// }
//
// public operator func -(t: Float32): Matrix {
// unsafe {
// let r = subBroadcastMatrix(M, t)
// return Matrix(r);
// }
// }
//
// public operator func *(s: Matrix): Matrix {
// unsafe {
// if (!(this == s)) {
// println('------------')
// print_shape()
// s.print_shape()
// println('------------')
// throw IllegalArgumentException("not same shape")
// }
// let r = multiMatrix(M, s.getData())
// return Matrix(r)
// }
// }
//
// public operator func *(t: Float32): Matrix {
// unsafe {
// let r = multiBroadcastMatrix(M, t)
// return Matrix(r)
// }
// }
//
// public operator func /(s: Matrix): Matrix {
// unsafe {
// if (!(this == s)) {
// println('------------')
// print_shape()
// s.print_shape()
// println('------------')
// throw IllegalArgumentException("not same shape")
// }
// let r = divMatrix(M, s.getData())
// return Matrix(r)
// }
// }
//
// public operator func /(t: Float32): Matrix {
// unsafe {
// let r = divBroadcastMatrix(M, t)
// return Matrix(r)
// }
// }
//
// /* -------------------------------- */
// /* -------------------------------- */
//
//
// /* -------- 矩阵点积和算法 -------- */
// public func dot (s: Matrix): Matrix {
// unsafe {
// if (!hasDotShapeWith(s)) {
// println('------------')
// print_shape()
// s.print_shape()
// println('------------')
// throw IllegalArgumentException("not dot shape")
// }
// let r = dotMatrix(M, s.getData())
// return Matrix(r)
// }
// }
//
// /* -------- 矩阵转置 -------- */
// public func T(): Matrix {
// unsafe {
// let r = transposeMatrix(M)
// return Matrix(r)
// }
// }
//
// /* -------- 打印矩阵 -------- */
// public func print(): Unit {
// unsafe {
// for (i in 0..getRow()) {
// for (j in 0..getCol()) {
// let a = this.get(i, j)
// print(a)
// print(" ")
// }
// }
// }
// println()
// }
//
// /* -------- 打印形状 -------- */
// public func print_shape() {
// println("${getRow()}, ${getCol()}")
// }
//
// /* -------- 保存矩阵 -------- */
// public func save(file: File) {
// unsafe {
// for (i in 0..getRow()) {
// for (j in 0..getCol()) {
// file.write(this.get(i, j).toString().toArray())
// if (j < getCol()-1) {
// file.write(" ".toArray()) }
// else { file.write("\n".toArray()) }
// }
// }
// }
// }
//
// /* -------- 静态方法,加载单独项 -------- */
// private static func loadItem(sr: StringReader<InputStream>) {
// let s = sr.readUntil({r => r==r' ' || r ==r'\n'})
// return Float32.parse(s.getOrThrow())
// }
//
// /* -------- 静态方法,加载矩阵 -------- */
// public static func load(sr: StringReader<InputStream>, row: UInt64, col: UInt64): Matrix {
// let m = Matrix(row, col)
// unsafe {
// for (i in 0..m.getRow()) {
// for (j in 0..m.getCol()) {
// let f = loadItem(sr)
// m.set(i, j, f)
// }
// }
// m
// }
// }
//
// /* -------- 矩阵的C风格指针 -------- */
// private var M: CPointer<Unit>
//}
+56
View File
@@ -0,0 +1,56 @@
package bot4cj.math
import std.collection.*
/* -------- 张量 --------
* INFO(20260524-qing): 输入一个维度列表 */
class Tensor<T> {
private let data: Array<T>
private let dims: Array<Int64>
private let size: Int64
public init(dims: Array<Int64>, val: T) {
var size = 1
for (item in dims) { /* ---- 计算总量 ---- */
size *= item
}
this.data = Array<T>(size, {_ => val})
this.dims = dims
this.size = size
}
/* -------- 私有构造函数,用于内部创建张量 -------- */
private init(dims: Array<Int64>, data: Array<T>) {
this.dims = dims
this.data = data
var size = 1
for (item in dims) {
size *= item
}
this.size = size
}
/* -------- 获取维度列表(形状) -------- */
public func shape(): Array<Int64> {
return dims
}
/* -------- 逐元素进行某种运算 -------- */
public func oneByOne(S: Tensor<T>, f: (T, T) -> T): Tensor<T> {
let s1 = S.shape()
/* ---- 比较维度数量 ---- */
if (s1 != dims) {
throw IllegalArgumentException("Tensors' shape are not same")
}
/* ---- 比较维度形状 ----*/
for (i in 0..dims.size) {
if (s1[i] != dims[i]) {
throw IllegalArgumentException("Tensors' shape are not same")
}
}
/* ---- 逐个进行计算 ---- */
let res_data = Array<T>(size, {i => f(data[i], S.data[i])})
return Tensor<T>(dims, res_data)
}
}
+379
View File
@@ -0,0 +1,379 @@
// cnn.cj - 修正 forward 中的偏置加法
package bot4cj.math
import std.math.*
import std.random.*
import std.collection.*
public class ConvLayer {
let nInputChannels: Int64
let nOutputChannels: Int64
let kernelHeight: Int64
let kernelWidth: Int64
let stride: Int64
let padding: Int64
let learningRate: FP32
let f: Af
var connectingMatrix: Matrix1
var kernels: Array<Matrix1>
var biases: Matrix1
var prevInput: Option<Matrix1> = Option.None
var prevInputShape: Option<(Int64, Int64, Int64, Int64)> = Option.None
var prevCombination: Option<Matrix1> = Option.None
var prevOutput: Option<Matrix1> = Option.None
var prevOutputShape: Option<(Int64, Int64, Int64, Int64)> = Option.None
var kernelsGrad: Option<Array<Matrix1>> = Option.None
var biasGrad: Option<Matrix1> = Option.None
public init(
inCh: Int64,
outCh: Int64,
kernelSize: (Int64, Int64),
stride: Int64,
padding: Int64,
lr: FP32,
activation: Af,
connMatrix: ?Matrix1
) {
nInputChannels = inCh
nOutputChannels = outCh
kernelHeight = kernelSize[0]
kernelWidth = kernelSize[1]
this.stride = stride
this.padding = padding
learningRate = lr
this.f = activation
// 初始化 connectingMatrix
connectingMatrix = match (connMatrix) {
case m: Matrix1 => m
case _ => Matrix1(nInputChannels, nOutputChannels)
}
let isNone = match (connMatrix) {
case _: Matrix1 => false
case _ => true
}
if (isNone) {
for (r in 0..nInputChannels) {
for (c in 0..nOutputChannels) {
connectingMatrix.set(r, c, FP32(1.0))
}
}
}
// 初始化 kernels
kernels = Array(nOutputChannels, { _ => Matrix1(1, 1) })
for (oc in 0..nOutputChannels) {
var nConnections = 0
for (ic in 0..nInputChannels) {
let val = connectingMatrix.get(ic, oc).getOrThrow()
if (val.getValue() > 0.0) {
nConnections += 1
}
}
let kernelLen = nConnections * kernelHeight * kernelWidth
let kernel = Matrix1(1, kernelLen)
Ini.xavierUniformInit(kernel, 1, kernelLen)
kernels[oc] = kernel
}
// 初始化 biases
biases = Matrix1(1, nOutputChannels)
Ini.xavierUniformInit(biases, 1, nOutputChannels)
}
// ==================== 前向传播 ====================
public func forward(x: Matrix1, inputShape: (Int64, Int64, Int64, Int64)): Matrix1 {
prevInput = Option.Some(x)
prevInputShape = Option.Some(inputShape)
let batch = inputShape[0]
let inH = inputShape[1]
let inW = inputShape[2]
let outH = (inH + 2 * padding - kernelHeight) / stride + 1
let outW = (inW + 2 * padding - kernelWidth) / stride + 1
let outSize = batch * outH * outW
var combination = Matrix1(outSize, nOutputChannels)
var output = Matrix1(outSize, nOutputChannels)
for (b in 0..batch) {
for (oh in 0..outH) {
for (ow in 0..outW) {
let outRow = b * outH * outW + oh * outW + ow
for (oc in 0..nOutputChannels) {
var sum = FP32(0.0)
var kernelIdx = 0
for (ic in 0..nInputChannels) {
let connVal = connectingMatrix.get(ic, oc).getOrThrow()
if (connVal.getValue() > 0.0) {
for (kh in 0..kernelHeight) {
for (kw in 0..kernelWidth) {
let ih = oh * stride + kh - padding
let iw = ow * stride + kw - padding
if (ih >= 0 && ih < inH && iw >= 0 && iw < inW) {
let inRow = b * inH * inW + ih * inW + iw
let xVal = x.get(inRow, ic).getOrThrow()
let kVal = kernels[oc].get(0, kernelIdx).getOrThrow()
sum = sum + xVal * kVal
}
kernelIdx += 1
}
}
}
}
combination.set(outRow, oc, sum)
}
}
}
}
// 手动广播偏置加法(因为 Matrix1 的 + 要求形状相同)
for (r in 0..outSize) {
for (c in 0..nOutputChannels) {
let val = combination.get(r, c).getOrThrow()
let biasVal = biases.get(0, c).getOrThrow()
combination.set(r, c, val + biasVal)
}
}
prevCombination = Option.Some(combination)
output = f.forward(combination)
prevOutput = Option.Some(output)
prevOutputShape = Option.Some((batch, outH, outW, nOutputChannels))
return output
}
// ==================== 反向传播 ====================
public func backward(errs: Matrix1): Matrix1 {
var x = Matrix1(1, 1)
match (prevInput) {
case Some(T) => x = T
case _ => throw Exception("No input data")
}
var shape = (0, 0, 0, 0)
match (prevInputShape) {
case Some(T) => shape = T
case _ => throw Exception("No input shape")
}
var combination = Matrix1(1, 1)
match (prevCombination) {
case Some(val) => combination = val
case _ => throw Exception("No combination")
}
var outShape = (0, 0, 0, 0)
match (prevOutputShape) {
case Some(val) => outShape = val
case _ => throw Exception("No output shape")
}
let batch = shape[0]
let inH = shape[1]
let inW = shape[2]
let outH = outShape[1]
let outW = outShape[2]
let delta = errs * f.backward(combination)
calcKernelGradient(x, delta, batch, inH, inW, outH, outW)
calcBiasGradient(delta)
let errsUp = calcUpsampledError(delta, batch, inH, inW, outH, outW)
return errsUp
}
/**
* 计算卷积核梯度
*/
private func calcKernelGradient(
x: Matrix1,
delta: Matrix1,
batch: Int64,
inH: Int64,
inW: Int64,
outH: Int64,
outW: Int64
): Unit {
var grads = Array(nOutputChannels, { _ => Matrix1(1, 1) })
for (oc in 0..nOutputChannels) {
let kernelCols = kernels[oc].get_col()
var grad = Matrix1(1, kernelCols)
for (b in 0..batch) {
for (oh in 0..outH) {
for (ow in 0..outW) {
let deltaRow = b * outH * outW + oh * outW + ow
let deltaVal = delta.get(deltaRow, oc).getOrThrow()
var kernelIdx = 0
for (ic in 0..nInputChannels) {
let connVal = connectingMatrix.get(ic, oc).getOrThrow()
if (connVal.getValue() > 0.0) {
for (kh in 0..kernelHeight) {
for (kw in 0..kernelWidth) {
let ih = oh * stride + kh - padding
let iw = ow * stride + kw - padding
if (ih >= 0 && ih < inH && iw >= 0 && iw < inW) {
let inRow = b * inH * inW + ih * inW + iw
let xVal = x.get(inRow, ic).getOrThrow()
let oldGrad = grad.get(0, kernelIdx).getOrThrow()
grad.set(0, kernelIdx, oldGrad + xVal * deltaVal)
}
kernelIdx += 1
}
}
}
}
}
}
}
grads[oc] = grad
}
kernelsGrad = Option.Some(grads)
}
/**
* 计算偏置梯度
*/
private func calcBiasGradient(delta: Matrix1): Unit {
let rows = delta.get_row()
var grad = Matrix1(1, nOutputChannels)
for (oc in 0..nOutputChannels) {
var sum = FP32(0.0)
for (r in 0..rows) {
sum = sum + delta.get(r, oc).getOrThrow()
}
grad.set(0, oc, sum)
}
biasGrad = Option.Some(grad)
}
/**
* 计算传递到上一层的误差
*/
private func calcUpsampledError(
delta: Matrix1,
batch: Int64,
inH: Int64,
inW: Int64,
outH: Int64,
outW: Int64
): Matrix1 {
let inSize = batch * inH * inW
var errsUp = Matrix1(inSize, nInputChannels)
for (b in 0..batch) {
for (oh in 0..outH) {
for (ow in 0..outW) {
let deltaRow = b * outH * outW + oh * outW + ow
for (oc in 0..nOutputChannels) {
let deltaVal = delta.get(deltaRow, oc).getOrThrow()
var kernelIdx = 0
for (ic in 0..nInputChannels) {
let connVal = connectingMatrix.get(ic, oc).getOrThrow()
if (connVal.getValue() > 0.0) {
for (kh in 0..kernelHeight) {
for (kw in 0..kernelWidth) {
let ih = oh * stride + kh - padding
let iw = ow * stride + kw - padding
if (ih >= 0 && ih < inH && iw >= 0 && iw < inW) {
let inRow = b * inH * inW + ih * inW + iw
let kVal = kernels[oc].get(0, kernelIdx).getOrThrow()
let oldErr = errsUp.get(inRow, ic).getOrThrow()
errsUp.set(inRow, ic, oldErr + deltaVal * kVal)
}
kernelIdx += 1
}
}
}
}
}
}
}
}
return errsUp
}
// ==================== 参数更新 ====================
public func update(discount: Float32): Unit {
var kGrad = Array(0, { _ => Matrix1(1, 1) })
match (kernelsGrad) {
case Some(val) => kGrad = val
case _ => throw Exception("No kernel gradients")
}
var bGrad = Matrix1(1, 1)
match (biasGrad) {
case Some(val) => bGrad = val
case _ => throw Exception("No bias gradient")
}
for (oc in 0..nOutputChannels) {
let rows = kernels[oc].get_row()
let cols = kernels[oc].get_col()
for (r in 0..rows) {
for (c in 0..cols) {
let oldVal = kernels[oc].get(r, c).getOrThrow()
let gradVal = kGrad[oc].get(r, c).getOrThrow()
kernels[oc].set(r, c, oldVal - gradVal * learningRate * FP32(discount))
}
}
}
for (oc in 0..nOutputChannels) {
let oldBias = biases.get(0, oc).getOrThrow()
let gradBias = bGrad.get(0, oc).getOrThrow()
biases.set(0, oc, oldBias - gradBias * learningRate * FP32(discount))
}
}
// ==================== 形状访问器 ====================
public func getInShape(): (Int64, Int64) {
(nInputChannels, kernelHeight * kernelWidth)
}
public func getOutShape(): Int64 {
nOutputChannels
}
public func getLR(): FP32 {
learningRate
}
public func getFType(): String {
f.get_type()
}
public func calculateOutputSize(inputHeight: Int64, inputWidth: Int64): (Int64, Int64) {
let outH = (inputHeight + 2 * padding - kernelHeight) / stride + 1
let outW = (inputWidth + 2 * padding - kernelWidth) / stride + 1
(outH, outW)
}
}
+11
View File
@@ -0,0 +1,11 @@
package bot4cj.math
//foreign func start_fft(M: CPointer<Unit>): CPointer<Unit>
//foreign func gen_wave(): CPointer<Unit>
/*-------- 生成测试用的正弦波 --------*/
//public func genWave(): Matrix {
// unsafe { return Matrix(gen_wave()) }
//}
/*-------- 执行fft算法 --------*/
//public func start_fft(M: Matrix): Matrix {
// unsafe { return Matrix(start_fft(M.getData())) }
//}
+136
View File
@@ -0,0 +1,136 @@
// flatten.cj - 移除调试代码
package bot4cj.math
import std.math.*
import std.collection.*
public class FlattenLayer {
let nInputChannels: Int64
let inputHeight: Int64
let inputWidth: Int64
let outputSize: Int64
var prevInput: Option<Matrix1>
var prevInputShape: Option<(Int64, Int64, Int64, Int64)>
var prevOutput: Option<Matrix1>
var prevOutputShape: Option<(Int64, Int64)>
public init(inputChannels: Int64, inputHeight: Int64, inputWidth: Int64) {
nInputChannels = inputChannels
this.inputHeight = inputHeight
this.inputWidth = inputWidth
outputSize = inputChannels * inputHeight * inputWidth
prevInput = None
prevInputShape = None
prevOutput = None
prevOutputShape = None
}
public init(inputShape: (Int64, Int64, Int64)) {
nInputChannels = inputShape[0]
inputHeight = inputShape[1]
inputWidth = inputShape[2]
outputSize = nInputChannels * inputHeight * inputWidth
prevInput = None
prevInputShape = None
prevOutput = None
prevOutputShape = None
}
public func forward(x: Matrix1, inputShape: (Int64, Int64, Int64, Int64)): Matrix1 {
prevInput = x
prevInputShape = inputShape
let batch = inputShape[0]
let inH = inputShape[1]
let inW = inputShape[2]
let inCh = inputShape[3]
let totalRows = batch * outputSize
var output = Matrix1(totalRows, 1)
for (b in 0..batch) {
for (h in 0..inH) {
for (w in 0..inW) {
for (ch in 0..inCh) {
let inRow = b * inH * inW + h * inW + w
let outRow = b * outputSize + (ch * inH * inW + h * inW + w)
if (outRow < totalRows) {
let val = x.get(inRow, ch).getOrThrow()
output.set(outRow, 0, val)
}
}
}
}
}
prevOutput = output
prevOutputShape = (totalRows, 1)
return output
}
public func backward(errs: Matrix1): Matrix1 {
var shape = (0, 0, 0, 0)
match (prevInputShape) {
case Some(T) => shape = T
case _ => throw Exception("FlattenLayer.backward: No input shape. Call forward() first.")
}
let batch = shape[0]
let inH = shape[1]
let inW = shape[2]
let inCh = shape[3]
let inSize = batch * inH * inW
var errsUp = Matrix1(inSize, nInputChannels)
let errsCols = errs.get_col()
for (b in 0..batch) {
for (h in 0..inH) {
for (w in 0..inW) {
let inRow = b * inH * inW + h * inW + w
for (ch in 0..inCh) {
let outRow = b * outputSize + (ch * inH * inW + h * inW + w)
if (outRow < errs.get_row()) {
if (errsCols == 1) {
let val = errs.get(outRow, 0).getOrThrow()
errsUp.set(inRow, ch, val)
} else {
var sum = FP32(0.0)
for (col in 0..errsCols) {
sum = sum + errs.get(outRow, col).getOrThrow()
}
errsUp.set(inRow, ch, sum)
}
}
}
}
}
}
return errsUp
}
public func update(discount: Float32): Unit {
// 无参数
}
public func getInShape(): (Int64, Int64, Int64) {
(nInputChannels, inputHeight, inputWidth)
}
public func getOutShape(): Int64 {
outputSize
}
public func getOutputSize(): Int64 {
outputSize
}
}
+69
View File
@@ -0,0 +1,69 @@
package bot4cj.math
/**
* 一维卡尔曼滤波器
* 使用时先定义一个kalman指针,然后调用kalmanCreate()创建一个滤波器
* 每次读取到传感器数据后即可调用KalmanFilter()来对数据进行滤波
* 使用示例
* extKalman_t p; //定义一个卡尔曼滤波器结构体
* float SersorData; //需要进行滤波的数据
* KalmanCreate(&p,20,200); //初始化该滤波器的Q=20 R=200参数
* while(1)
* {
* SersorData = sersor(); //获取数据
* SersorData = KalmanFilter(&p,SersorData); //对数据进行滤波
* }
*/
@C
public struct extKalman {
var X_last: Float32 // 上一时刻的最优结果 X(k-|k-1)
var P_last: Float32 // 上一时刻最优结果的协方差 P(k-1|k-1)
var A: Float32 // 系统参数
var B: Float32
var Q: Float32
var R: Float32
var H: Float32
/**
* @name kalmanCreate
* @brief 创建一个卡尔曼滤波器
* @param p: 滤波器
* T_Q:系统噪声协方差
* T_R:测量噪声协方差
*
* @retval none
* @attention R固定,Q越大,代表越信任侧量值,Q无穷代表只用测量值
* 反之,Q越小代表越信任模型预测值,Q为零则是只用模型预测
*/
public init(T_Q: Float32, T_R: Float32) {
X_last = 0.0f32
P_last = 0.0f32
Q = T_Q
R = T_R
A = 1.0f32
B = 0.0f32
H = 1.0f32
}
/**
* @name KalmanFilter
* @brief 卡尔曼滤波器
* @param p: 滤波器
* dat:待滤波数据
* @retval 滤波后的数据
* @attention Z(k)是系统输入,即测量值 X(k|k)是卡尔曼滤波后的值,即最终输出
* A=1 B=0 H=1 I=1 W(K) V(k)是高斯白噪声,叠加在测量值上了,可以不用管
* 以下是卡尔曼的5个核心公式
* 一阶H'即为它本身,否则为转置矩阵
*/
public mut func filter(dat: Float32): Float32 {
let X_mid = A * X_last //百度对应公式(1) x(k|k-1) = A*X(k-1|k-1)+B*U(k)+W(K)
let P_mid = A * P_last + Q //百度对应公式(2) p(k|k-1) = A*p(k-1|k-1)*A'+Q
let kg = P_mid / (P_mid + R) //百度对应公式(4) kg(k) = p(k|k-1)*H'/(H*p(k|k-1)*H'+R)
let X_now = X_mid + kg * (dat - X_mid) //百度对应公式(3) x(k|k) = X(k|k-1)+kg(k)*(Z(k)-H*X(k|k-1))
let P_now = (1.0f32 - kg) * P_mid //百度对应公式(5) p(k|k) = (I-kg(k)*H)*P(k|k-1)
P_last = P_now
X_last = X_now
return X_now //输出预测结果x(k|k)
}
}
+331
View File
@@ -0,0 +1,331 @@
package bot4cj.math
import std.fs.*
import std.io.*
import std.convert.*
//foreign func Sigmoid_do(m: CPointer<Unit>): CPointer<Unit>
//foreign func Sigmoid_dot(m: CPointer<Unit>): CPointer<Unit>
//foreign func ReLU_do(m: CPointer<Unit>): CPointer<Unit>
//foreign func ReLU_dot(m: CPointer<Unit>): CPointer<Unit>
//foreign func Leaky_ReLU_do(m: CPointer<Unit>, alpha: Float32): CPointer<Unit>
//foreign func Leaky_ReLU_dot(m: CPointer<Unit>, alpha: Float32): CPointer<Unit>
//foreign func Tanh_do(m: CPointer<Unit>): CPointer<Unit>
//foreign func Tanh_dot(m: CPointer<Unit>): CPointer<Unit>
/* -------- 新版的神经网络,使用原生矩阵对象实现 --------
* TODO(20260517-qing): 增加batch批次处理,使用ArrayList<Matrix1>
* TODO*(20260519-qing): 将神经网络转换为MatrixObject矩阵式对象
* TODO(20260524-qing): 增加Adam优化器 */
public class Nn1 {
let inputno: Int64 /* -------- 输入 -------- */
let outputno: Int64 /* -------- 输出 -------- */
var weights: Matrix1 /* -- (inputs, outputs) - */
var bias: Matrix1 /* ----- (1, output) ---- */
let learning_rate: FP32 /* ------- 学习率 ------- */
let f: Af /* ------ 激活函数 ------ */
/*------------------*/
var prev_inputs: Option<Matrix1> = None /* ---- 之前的输入 ---- */
var prev_raw_inputs: Option<Matrix1> = None /* ---- 之前的原始输出 ---- */
var prev_outputs: Option<Matrix1> = None /* ---- 之前的输出 ---- */
/*------------------*/
var weights_grad: Option<Matrix1> = None /* ---- 权重梯度 ---- */
var bias_grad: Option<Matrix1> = None /* ---- 偏置梯度 ---- */
/* -------- 初始化器 --------
* TODO(20260516-qing): 不要把初始化方法写死,使用枚举类 */
public init(i: Int64, o: Int64, lr: Float32, f: Af) {
inputno = i
outputno =o
learning_rate=FP32(lr)
this.f = f
weights = Matrix1(inputno, outputno)
bias = Matrix1(1, outputno)
/* -------- 初始化权重 -------- */
Ini.xavierUniformInit(weights, inputno, outputno)
}
/* -------- 神经网络推理 -------- */
public func forward(x: Matrix1) {
prev_inputs = x
var r = x.dot(weights)
r = r + bias
prev_raw_inputs = r
let r1 = f.forward(r)
prev_outputs = r1
return r1
}
/* -------- 反向传播算法 -------- */
public func backward(errs: Matrix1)
{
if (is_softmax(f)) {
return softmax_backward(errs)
}
let fbak = f.backward(prev_raw_inputs.getOrThrow())
let first = errs * fbak /* 逐元素相乘 */
let mx = prev_inputs.getOrThrow().T()
weights_grad = mx.dot(first)
bias_grad = first
let errs_up = first.dot(weights.T())
return errs_up
}
/* -------- 更新权重值、偏爱因子 --------
* NOTE: discount(折扣)是为了方便深度强化学习给出奖励值 */
public func update(discount: Float32) {
weights = weights - weights_grad.getOrThrow() * learning_rate * FP32(discount)
bias = bias - bias_grad.getOrThrow() * learning_rate * FP32(discount)
}
/**/
private static func is_softmax(f: Af) {
return match (f) {
case Af.Softmax => true
case _ => false
}
}
private func softmax_backward(errs: Matrix1) {
/* 使用雅可比矩阵的方式 */
let y = prev_outputs.getOrThrow() /* softmax 输出 */
let dy = errs /* 上游梯度 ∂L/∂y */
let jacobian = f.backward(y) /* n×n 雅可比矩阵 */
/* 将 dy 展平成列向量 n×1 */
let row = dy.get_row()
let col = dy.get_col()
let n = row * col
var dy_vec = Matrix1(n, 1)
var idx = 0
for (r in 0..row) {
for (c in 0..col) {
let val = dy.get(r, c).getOrThrow()
dy_vec.set(idx, 0, val)
idx = idx + 1
}
}
/* dx_vec = J · dy_vec (n×n × n×1 = n×1) */
var dx_vec = Matrix1(n, 1)
for (i in 0..n) {
var sum = 0.0f32
for (j in 0..n) {
let j_val = jacobian.get(i, j).getOrThrow().getValue()
let dy_val = dy_vec.get(j, 0).getOrThrow().getValue()
sum = sum + j_val * dy_val
}
dx_vec.set(i, 0, FP32(sum))
}
/* 恢复为原始形状,作为 first */
var first = Matrix1(row, col)
idx = 0
for (r in 0..row) {
for (c in 0..col) {
let val = dx_vec.get(idx, 0).getOrThrow()
first.set(r, c, val)
idx = idx + 1
}
}
let mx = prev_inputs.getOrThrow().T()
weights_grad = mx.dot(first)
bias_grad = first
let errs_up = first.dot(weights.T())
return errs_up
}
/* -------- 形状访问器 -------- */
public func get_in_shape() {
return inputno }
public func get_out_shape() {
return outputno }
public func get_lr() {
return learning_rate }
public func get_f_type() {
return f.get_type() }
public func get_f_val() {
return f.get_value() }
/* ---------------------------- */
}
///* -------- 旧版激活函数 --------*/
//public enum ActivationFuncs {
// Sigmoid | ReLU | Leaky_ReLU | Tanh
// func fdo(m: Matrix): Matrix { // 暂时只设置ReLU
// unsafe {
// match(this) {
// case Sigmoid => Matrix(Sigmoid_do(m.getData()))
// case ReLU => Matrix(ReLU_do(m.getData()))
// case Leaky_ReLU => Matrix(Leaky_ReLU_do(m.getData(), 0.1f32))
// case Tanh => Matrix(Tanh_do(m.getData()))
// }
// }
// }
// func fdot(m: Matrix): Matrix {
// unsafe {
// match(this) {
// case Sigmoid => Matrix(Sigmoid_dot(m.getData()))
// case ReLU => Matrix(ReLU_dot(m.getData()))
// case Leaky_ReLU => Matrix(Leaky_ReLU_dot(m.getData(), 0.1f32))
// case Tanh => Matrix(Tanh_dot(m.getData()))
// }
// }
// }
// func toString(): String {
// match(this) {
// case Sigmoid => "Sigmoid"
// case ReLU => "ReLU"
// case Leaky_ReLU => "Leaky_ReLU"
// case Tanh => "Tanh"
// }
// }
// static func parse(s: String) {
// match(s) {
// case "Sigmoid" => ActivationFuncs.Sigmoid
// case "ReLU" => ActivationFuncs.ReLU
// case "Leaky_ReLU" => ActivationFuncs.Leaky_ReLU
// case "Tanh" => ActivationFuncs.Tanh
// case _ => throw IllegalArgumentException()
// }
// }
//}
///* -------- 旧版的神经网络类 --------
// * 这个实现依赖C++语言的动态分享库,特点是速度快
// * 这个实现可能是有问题的 */
//public class Nn {
//
// /* -------- 主初始化器 -------- */
// public init(
// inputno: UInt64, outputno: UInt64, f: ActivationFuncs, learning_rate: Float32) {
// this.inputno = inputno
// this.outputno = outputno
// weights = Matrix.initWithXavierUniform(inputno, outputno)
// bias = Matrix(1, outputno)
// this.f = f
// this.learning_rate = learning_rate
// }
//
// /* -------- 提供了权重矩阵的初始化器 -------- */
// public init(
// inputno: UInt64, outputno: UInt64, f: ActivationFuncs, learning_rate: Float32, weights: Matrix, bias: Matrix) {
// this.inputno = inputno
// this.outputno = outputno
// this.weights = weights
// this.bias = bias
// this.f = f
// this.learning_rate = learning_rate
// }
//
// /* -------- 神经网络推理 -------- */
// func forward(x: Matrix): Matrix {
// /* x => (1, inputs) */
// let r = x.dot(weights) + bias /* (1, outputno) */
// return r
// }
//
// /* -------- 实施激活函数 -------- */
// func fdo(x: Matrix): Matrix {
// return f.fdo(x) }
//
// /* -------- 激活函数导数 -------- */
// func fdot(x: Matrix): Matrix {
// return f.fdot(x) }
//
// /* This is Wrong */
// //func bak_bsgrad(prev_raw_outputs_dotf: Matrix, errs: Matrix): Matrix {
// /* prev_raw_outputs_dotf => (1, outputno)
// * errs => (1, outputno) */
//
// // return errs * prev_raw_outputs_dotf
// //}
//
// /* -------- 权重梯度的传播 -------- */
// func bak_wsgrad(prev_inputs: Matrix, delta: Matrix) {
// /* prev_inputs => (1, inputs) */
// return prev_inputs.T().dot(delta) /* (inputs, outputs) */
// }
//
// /* -------- 计算上一层的误差 -------- */
// func bak_uperrs(delta: Matrix, prev_dotf: Matrix) {
// let first = weights.dot(delta.T()).T() // (1, inputs)
//
// var uperrs = first * prev_dotf
// uperrs
// }
//
// /* -------- 更新权重、偏爱因子 -------- */
// func update(weights_grad: Matrix, bias_grad: Matrix, discount: Float32) {
// weights -= weights_grad * learning_rate * discount
// bias -= bias_grad * learning_rate * discount
// }
//
// /* -------- 打印权重、偏爱因子 -------- */
// func print() {
// weights.print()
// bias.print()
// }
//
// /* -------- 打印神经网络全连接层的形状 -------- */
// func print_shape() {
// println("${inputno}, ${outputno}") }
//
// /* -------- 储存神经网络 -------- */
// func save(file: File) {
// file.write("FullConnectLayer ".toArray())
// file.write(inputno.toString().toArray())
// file.write(" ".toArray())
// file.write(outputno.toString().toArray())
// file.write(" ".toArray())
// file.write(learning_rate.toString().toArray())
// file.write(" ".toArray())
// file.write(f.toString().toArray())
// file.write("\n".toArray())
// weights.save(file)
// //file.write("\n".toArray())
// bias.save(file)
// //file.write("\n".toArray())
// }
//
// /* -------- 静态方法,加载单个项目 -------- */
// static func loadItem<T>(sr: StringReader<InputStream>) where T <: Parsable<T>
// {
// let s = sr.readUntil({r => r == r" " || r == r"\n"})
// let ss = s.getOrThrow().trimAscii()
// println(ss)
// return T.parse(ss)
// }
//
// /* -------- 静态方法,加载神经网络 -------- */
// static func load(sr: StringReader<InputStream>)
// {
// let inputno = loadItem<UInt64>(sr)
// let outputno = loadItem<UInt64>(sr)
// let lr = loadItem<Float32>(sr)
// let fna = sr.readUntil(r'\n')
// let f = ActivationFuncs.parse(fna.getOrThrow().trimAscii())
// let weights = Matrix.load(sr, inputno, outputno)
// let bias = Matrix.load(sr, 1, outputno)
// Nn(inputno, outputno, f, lr, weights, bias)
// }
//
// let inputno: UInt64
// let outputno: UInt64
// var weights: Matrix /* (inputs, outputs) */
// var bias: Matrix /* (1, outputs) */
// var learning_rate: Float32
// let f: ActivationFuncs
//}
+164
View File
@@ -0,0 +1,164 @@
package bot4cj.math
import std.io.*
import std.fs.*
import std.math.*
import std.time.*
import std.random.*
import std.collection.*
/* -------- 准备与门的测试用例 --------
* INFO(20260524-qing): 输入{2},输出{1} */
public func prepare_andDoor(m: Matrix1, t: Matrix1) {
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.0f32 } else { 0.0f32 }
//let t1 = if (a != b) { 1.0f32 } else { 0.0f32 }
m.set(0, 0, FP32(Float32(a)))
m.set(0, 1, FP32(Float32(b)))
t.set(0, 0, FP32(t0))
//t.set(0, 1, FP32(t1))
}
/* -------- 新版神经网络推理,使用原生矩阵 -------- */
public func forward1(nns: ArrayList<Nn1>, m: Matrix1) {
var out_dof = Matrix1(1, 1) /* ---- 给一个值占位 ----*/
for (idx in 0..nns.size) {
let x = if (idx == 0) { m } else { out_dof }
out_dof= nns[idx].forward(x)
}
return out_dof
}
/* 【神经网络训练】
* description: 输入一组原始数据和教师数据,进行神经网络训练
* @param nns: 输入神经网络结构
* @param m: 输入一组原始数据
* @param t: 输入一组教师数据
* @param discount: 输入本次训练的折扣系数
*/
public func train1(nns: ArrayList<Nn1>, m: Matrix1, t: Matrix1, discount: Float32) {
/* ======== 这个对象用于暂时占位 ======== */
var res = Matrix1(1, 1)
/* ======== 正向传播 ======== */
for (idx in 0..nns.size) {
let x = if (idx == 0) { m } else { res }
res = nns[idx].forward(x)
}
/* ======== 计算误差 ======== */
var errs = t - res
/* ======== 反向传播 ======== */
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).getOrThrow().getValue()
//let t1 = t.get(0, 1).getOrThrow().getValue()
let r0 = res.get(0, 0).getOrThrow().getValue()
//let r1 = res.get(0, 1).getOrThrow().getValue()
let loss0 = 0.5 * (t0 - r0) * (t0 - r0)
//let loss1 = 0.5 * (t1 - r1) * (t1 - r1)
//return (loss0 + loss1) / 2.0
return loss0
}
/* -------- (旧版)加载多层神经网络 --------
TODO: 也许用类封装
INFO(20260517-qing): 新版使用仓颉原生矩阵,然后与MatrixObject类互转 */
//public func load_nns(filename: String): ArrayList<Nn> {
// let nns = ArrayList<Nn>()
// let file = File(filename, OpenMode.Read)
// let sr = StringReader<InputStream>(file)
// while (true) {
// if (let Some(S) <- sr.readUntil(r' ')) {
// var S2 = S.trimAscii()
// if (S2 == "FullConnectLayer") {
// nns.add(Nn.load(sr)) }
// else if (S2 == "") { break }
// else { throw IllegalArgumentException() }
// }
// else { break }
// }
// file.close()
// return nns
//}
/* -------- (旧版)储存多层神经网络 -------- */
//public func save_nns(nns: ArrayList<Nn>, filename: String) {
//
// let file: File
// if (exists(filename)) {
// file = File(filename, OpenMode.Write) }
// else { file= File.create(filename) }
// for (nn in nns) { /* 存储全部的层 */
// nn.save(file) }
// file.close()
//}
/* -------- 旧版神经网络推理 -------- */
//public func forward(nns: ArrayList<Nn>, m: Matrix) {
// var out_dof = Matrix(1, 1)
// for (idx in 0..nns.size) {
// let x = if (idx == 0) { m } else { out_dof }
// let res = nns[idx].forward(x)
// out_dof = nns[idx].fdo(res)
// }
// return out_dof
//}
/* -------- 旧版的神经网络训练 -------- */
//public func train(nns: ArrayList<Nn>, m: Matrix, t: Matrix, discount: Float32) {
//
// /* ----------------- */
// let outs_dof = ArrayList<Matrix> ()
// let outs_dotf = ArrayList<Matrix> ()
//
// /* -------- 正向传播 -------- */
// for (idx in 0..nns.size) {
// let x = if (idx == 0) { m } else { outs_dof[idx-1] }
// let res = nns[idx].forward(x)
// let out_dof = nns[idx].fdo(res)
// let out_dotf = nns[idx].fdot(out_dof)
// outs_dof.add(out_dof)
// outs_dotf.add(out_dotf)
// }
//
// /* -------- 计算误差 -------- */
// var errs = outs_dof[nns.size-1] - t
//
// /* -------- 反向传播 -------- */
// for (idx in (nns.size-1)..=0:-1) {
// let up = if (idx == 0) { m} else { outs_dof[idx-1] }
// let bias_grad = errs
// //let delta = nns[idx].bak_bsgrad(errs, outs_dotf[idx])
// let weights_grad = nns[idx].bak_wsgrad(up, bias_grad)
// if (idx > 0) {
// errs = nns[idx].bak_uperrs(bias_grad, outs_dotf[idx-1]) }
// nns[idx].update(weights_grad, bias_grad, discount)
// }
//
// /* -------- 输出损失 -------- */
// unsafe {
// let t0 = t.get(0, 0)
// let t1 = t.get(0, 1)
// let r0 = outs_dof[nns.size-1].get(0, 0)
// let r1 = outs_dof[nns.size-1].get(0, 1)
// let loss0 = -t0 * log(r0) - (1.0f32-t0) * log(1.0f32-r0);
// let loss1 = -t1 * log(r1) - (1.0f32-t1) * log(1.0f32-r1);
// return 0.5 * (loss0 * loss0 + loss1 * loss1)
// }
//
//}
+418
View File
@@ -0,0 +1,418 @@
// pooling_layer.cj
package bot4cj.math
import std.math.*
import std.collection.*
/**
* 池化层类
* 设计风格与 Nn1 和 ConvLayer 保持一致
*/
public class PoolingLayer {
// ==================== 层配置参数 ====================
let nChannels: Int64
let poolHeight: Int64
let poolWidth: Int64
let stride: Int64
let padding: Int64
let poolType: String
let f: Af
let learningRateWeight: FP32
let learningRateBias: FP32
// ==================== 可学习参数 ====================
var weights: Matrix1
var biases: Matrix1
// ==================== 运行时缓存 ====================
var prevInput: Option<Matrix1> = Option.None
var prevInputShape: Option<(Int64, Int64, Int64, Int64)> = Option.None
var prevCombination: Option<Matrix1> = Option.None
var prevOutput: Option<Matrix1> = Option.None
var prevOutputShape: Option<(Int64, Int64, Int64, Int64)> = Option.None
var prevMaxIndices: Option<Matrix1> = Option.None
var weightsGrad: Option<Matrix1> = Option.None
var biasGrad: Option<Matrix1> = Option.None
// ==================== 构造函数 ====================
public init(
nChannels: Int64,
poolSize: (Int64, Int64),
stride: Int64,
padding: Int64,
poolType: String,
activation: Af,
lrWeight: FP32,
lrBias: FP32
) {
this.nChannels = nChannels
poolHeight = poolSize[0]
poolWidth = poolSize[1]
this.stride = stride
this.padding = padding
this.poolType = poolType
this.f = activation
learningRateWeight = lrWeight
learningRateBias = lrBias
// 初始化权值
weights = Matrix1(1, nChannels)
if (poolType == "average") {
let avgWeight = FP32(1.0) / FP32(Float32(poolHeight * poolWidth))
for (c in 0..nChannels) {
weights.set(0, c, avgWeight)
}
} else {
for (c in 0..nChannels) {
weights.set(0, c, FP32(1.0))
}
}
// 初始化偏置为 0
biases = Matrix1(1, nChannels)
for (c in 0..nChannels) {
biases.set(0, c, FP32(0.0))
}
}
public init(
nChannels: Int64,
poolSize: (Int64, Int64),
poolType: String,
activation: Af
) {
this.nChannels = nChannels
poolHeight = poolSize[0]
poolWidth = poolSize[1]
stride = poolHeight
padding = 0
this.poolType = poolType
this.f = activation
learningRateWeight = FP32(0.1)
learningRateBias = FP32(0.1)
weights = Matrix1(1, nChannels)
if (poolType == "average") {
let avgWeight = FP32(1.0) / FP32(Float32(poolHeight * poolWidth))
for (c in 0..nChannels) {
weights.set(0, c, avgWeight)
}
} else {
for (c in 0..nChannels) {
weights.set(0, c, FP32(1.0))
}
}
biases = Matrix1(1, nChannels)
for (c in 0..nChannels) {
biases.set(0, c, FP32(0.0))
}
}
// ==================== 前向传播 ====================
public func forward(x: Matrix1, inputShape: (Int64, Int64, Int64, Int64)): Matrix1 {
prevInput = Option.Some(x)
prevInputShape = Option.Some(inputShape)
let batch = inputShape[0]
let inH = inputShape[1]
let inW = inputShape[2]
let outH = (inH + 2 * padding - poolHeight) / stride + 1
let outW = (inW + 2 * padding - poolWidth) / stride + 1
let outSize = batch * outH * outW
var combination = Matrix1(outSize, nChannels)
var output = Matrix1(outSize, nChannels)
var maxIndices = Matrix1(outSize, nChannels)
for (b in 0..batch) {
for (oh in 0..outH) {
for (ow in 0..outW) {
let outRow = b * outH * outW + oh * outW + ow
for (ch in 0..nChannels) {
var pooledVal = FP32(0.0)
var maxIdx = 0
if (poolType == "max") {
var maxVal = FP32(-1.0e30)
var kernelIdx = 0
for (kh in 0..poolHeight) {
for (kw in 0..poolWidth) {
let ih = oh * stride + kh - padding
let iw = ow * stride + kw - padding
if (ih >= 0 && ih < inH && iw >= 0 && iw < inW) {
let inRow = b * inH * inW + ih * inW + iw
let val = x.get(inRow, ch).getOrThrow()
if (val.getValue() > maxVal.getValue()) {
maxVal = val
maxIdx = kernelIdx
}
}
kernelIdx += 1
}
}
pooledVal = maxVal
} else {
var sum = FP32(0.0)
var count = 0
for (kh in 0..poolHeight) {
for (kw in 0..poolWidth) {
let ih = oh * stride + kh - padding
let iw = ow * stride + kw - padding
if (ih >= 0 && ih < inH && iw >= 0 && iw < inW) {
let inRow = b * inH * inW + ih * inW + iw
sum = sum + x.get(inRow, ch).getOrThrow()
count += 1
}
}
}
if (count > 0) {
pooledVal = sum / FP32(Float32(count))
}
}
combination.set(outRow, ch, pooledVal)
maxIndices.set(outRow, ch, FP32(Float32(maxIdx)))
}
}
}
}
// 逐通道应用权值和偏置
for (r in 0..outSize) {
for (ch in 0..nChannels) {
let val = combination.get(r, ch).getOrThrow()
let w = weights.get(0, ch).getOrThrow()
let b = biases.get(0, ch).getOrThrow()
combination.set(r, ch, val * w + b)
}
}
prevCombination = Option.Some(combination)
output = f.forward(combination)
prevOutput = Option.Some(output)
prevOutputShape = Option.Some((batch, outH, outW, nChannels))
prevMaxIndices = Option.Some(maxIndices)
return output
}
// ==================== 反向传播 ====================
public func backward(errs: Matrix1): Matrix1 {
var x = Matrix1(1, 1)
match (prevInput) {
case Some(val) => x = val
case _ => throw Exception("No input data")
}
var shape = (0, 0, 0, 0)
match (prevInputShape) {
case Some(val) => shape = val
case _ => throw Exception("No input shape")
}
var combination = Matrix1(1, 1)
match (prevCombination) {
case Some(val) => combination = val
case _ => throw Exception("No combination")
}
var outShape = (0, 0, 0, 0)
match (prevOutputShape) {
case Some(val) => outShape = val
case _ => throw Exception("No output shape")
}
var maxIndices = Matrix1(1, 1)
let hasMaxIndices = match (prevMaxIndices) {
case Some(_) => true
case _ => false
}
if (hasMaxIndices) {
maxIndices = match (prevMaxIndices) {
case Some(val) => val
case _ => Matrix1(1, 1)
}
}
let batch = shape[0]
let inH = shape[1]
let inW = shape[2]
let outH = outShape[1]
let outW = outShape[2]
let delta = errs * f.backward(combination)
calcWeightBiasGradient(delta, combination)
let errsUp = calcUpsampledError(delta, maxIndices, batch, inH, inW, outH, outW)
return errsUp
}
/**
* 计算权值和偏置梯度
*/
private func calcWeightBiasGradient(delta: Matrix1, combination: Matrix1): Unit {
let outSize = delta.get_row()
var wGrad = Matrix1(1, nChannels)
var bGrad = Matrix1(1, nChannels)
for (ch in 0..nChannels) {
var wSum = FP32(0.0)
var bSum = FP32(0.0)
for (r in 0..outSize) {
let d = delta.get(r, ch).getOrThrow()
bSum = bSum + d
let comb = combination.get(r, ch).getOrThrow()
wSum = wSum + d * comb
}
wGrad.set(0, ch, wSum)
bGrad.set(0, ch, bSum)
}
weightsGrad = Option.Some(wGrad)
biasGrad = Option.Some(bGrad)
}
/**
* 上采样误差(池化层的反向传播核心)
*/
private func calcUpsampledError(
delta: Matrix1,
maxIndices: Matrix1,
batch: Int64,
inH: Int64,
inW: Int64,
outH: Int64,
outW: Int64
): Matrix1 {
let inSize = batch * inH * inW
var errsUp = Matrix1(inSize, nChannels)
for (b in 0..batch) {
for (oh in 0..outH) {
for (ow in 0..outW) {
let deltaRow = b * outH * outW + oh * outW + ow
for (ch in 0..nChannels) {
let deltaVal = delta.get(deltaRow, ch).getOrThrow()
let w = weights.get(0, ch).getOrThrow()
if (poolType == "max") {
let maxIdx = Int64(maxIndices.get(deltaRow, ch).getOrThrow().getValue())
var kernelIdx = 0
var found = false
for (kh in 0..poolHeight) {
for (kw in 0..poolWidth) {
if (kernelIdx == maxIdx && !found) {
let ih = oh * stride + kh - padding
let iw = ow * stride + kw - padding
if (ih >= 0 && ih < inH && iw >= 0 && iw < inW) {
let inRow = b * inH * inW + ih * inW + iw
errsUp.set(inRow, ch, deltaVal * w)
found = true
}
}
kernelIdx += 1
}
}
} else {
var count = 0
for (kh in 0..poolHeight) {
for (kw in 0..poolWidth) {
let ih = oh * stride + kh - padding
let iw = ow * stride + kw - padding
if (ih >= 0 && ih < inH && iw >= 0 && iw < inW) {
count += 1
}
}
}
if (count > 0) {
let avgDelta = deltaVal * w / FP32(Float32(count))
for (kh in 0..poolHeight) {
for (kw in 0..poolWidth) {
let ih = oh * stride + kh - padding
let iw = ow * stride + kw - padding
if (ih >= 0 && ih < inH && iw >= 0 && iw < inW) {
let inRow = b * inH * inW + ih * inW + iw
let oldVal = errsUp.get(inRow, ch).getOrThrow()
errsUp.set(inRow, ch, oldVal + avgDelta)
}
}
}
}
}
}
}
}
}
return errsUp
}
// ==================== 参数更新 ====================
public func update(discount: Float32): Unit {
var wGrad = Matrix1(1, 1)
match (weightsGrad) {
case Some(val) => wGrad = val
case _ => throw Exception("No weight gradients")
}
var bGrad = Matrix1(1, 1)
match (biasGrad) {
case Some(val) => bGrad = val
case _ => throw Exception("No bias gradients")
}
for (ch in 0..nChannels) {
let oldW = weights.get(0, ch).getOrThrow()
let gW = wGrad.get(0, ch).getOrThrow()
weights.set(0, ch, oldW + gW * learningRateWeight * FP32(discount))
}
for (ch in 0..nChannels) {
let oldB = biases.get(0, ch).getOrThrow()
let gB = bGrad.get(0, ch).getOrThrow()
biases.set(0, ch, oldB + gB * learningRateBias * FP32(discount))
}
}
// ==================== 形状访问器 ====================
public func getInShape(): (Int64, Int64) {
(nChannels, poolHeight * poolWidth)
}
public func getOutShape(): Int64 {
nChannels
}
public func getPoolType(): String {
poolType
}
public func getLR(): FP32 {
learningRateWeight
}
public func getFType(): String {
f.get_type()
}
public func calculateOutputSize(inputHeight: Int64, inputWidth: Int64): (Int64, Int64) {
let outH = (inputHeight + 2 * padding - poolHeight) / stride + 1
let outW = (inputWidth + 2 * padding - poolWidth) / stride + 1
(outH, outW)
}
}
+1071
View File
File diff suppressed because it is too large. Load diff
+310
View File
@@ -0,0 +1,310 @@
// test_cnn.cj - 使用 Adam 优化器的版本(所有层)
package bot4cj
import bot4cj.math.*
import std.math.*
import std.random.*
import std.collection.*
func generateSolidColorData(numSamples: Int64): (Array<Matrix1>, Array<Int64>) {
var inputs = Array(numSamples, { _ => Matrix1(1, 1) })
var labels = Array(numSamples, { _ => 0 })
let rng = Random()
for (i in 0..numSamples) {
let colorClass = Int64(rng.nextFloat64() * 3.0)
labels[i] = colorClass
let h = 8
let w = 8
var matrix = Matrix1(h * w, 1)
let colorValue = match (colorClass) {
case 0 => FP32(1.0)
case 1 => FP32(0.5)
case _ => FP32(0.0)
}
for (r in 0..h * w) {
let noise = FP32(Float32(rng.nextFloat64() - 0.5) * 0.1)
var val = colorValue + noise
if (val.getValue() > 1.0) {
val = FP32(1.0)
} else if (val.getValue() < 0.0) {
val = FP32(0.0)
}
matrix.set(r, 0, val)
}
inputs[i] = matrix
}
(inputs, labels)
}
func printSeparator(): Unit {
println("==================================================")
}
func testCNN(): Unit {
printSeparator()
println("CNN 纯色矩阵识别测试 (所有层使用 Adam 优化器)")
printSeparator()
println()
let nTrainSamples = 200
let nTestSamples = 60
let nEpochs = 50
let batchSize = 10
println("训练样本数: ${nTrainSamples}")
println("测试样本数: ${nTestSamples}")
println("训练轮数: ${nEpochs}")
println("Batch 大小: ${batchSize}")
println()
println("创建网络结构...")
println("Input(8x8x1) -> Conv(3x3,1->4) -> Pool(2x2) -> Conv(3x3,4->8) -> Flatten -> FC(8,3)")
println()
let lr = FP32(0.001)
var conv1 = ConvLayer(1, 4, (3, 3), 1, 0, lr, Af.Leaky_ReLU(FP32(0.01)), Option.None)
var pool1 = PoolingLayer(4, (2, 2), 2, 0, "max", Af.ReLU, lr, lr)
var conv2 = ConvLayer(4, 8, (3, 3), 1, 0, lr, Af.Leaky_ReLU(FP32(0.01)), Option.None)
var flatten = FlattenLayer(8, 1, 1)
var fc = Nn1(8, 3, Float32(0.4), Af.Softmax)
// 创建 Adam 优化器(为每个可训练层创建独立的优化器)
var adamConv1 = Adam(FP32(0.001)) // 卷积层1
var adamConv2 = Adam(FP32(0.001)) // 卷积层2
var adamFC = Adam(FP32(0.0005)) // 全连接层(使用更小的学习率)
println("网络创建完成 (Conv1, Conv2, FC 使用 Adam 优化器)")
println()
println("生成数据...")
let (trainInputs, trainLabels) = generateSolidColorData(nTrainSamples)
let (testInputs, testLabels) = generateSolidColorData(nTestSamples)
println("训练集: ${nTrainSamples} 样本")
println("测试集: ${nTestSamples} 样本")
println()
println("开始训练...")
println()
for (epoch in 0..nEpochs) {
var epochLoss = Float32(0.0)
var epochCorrect = 0
for (i in 0..nTrainSamples) {
let x = trainInputs[i]
let label = trainLabels[i]
// ===== 前向传播 =====
let c1Out = conv1.forward(x, (1, 8, 8, 1))
let p1Out = pool1.forward(c1Out, (1, 6, 6, 4))
let c2Out = conv2.forward(p1Out, (1, 3, 3, 4))
let flatOut = flatten.forward(c2Out, (1, 1, 1, 8))
// 调试:确认 flatten.forward 执行成功
if (i == 0 && epoch == 0) {
println("DEBUG: flatten.forward done, flatOut rows: ${flatOut.get_row()}, cols: ${flatOut.get_col()}")
}
var fcInput = Matrix1(1, 8)
for (j in 0..8) {
fcInput.set(0, j, flatOut.get(j, 0).getOrThrow())
}
let fcOut = fc.forward(fcInput)
// Softmax
var softmax = Matrix1(1, 3)
var maxVal = Float32(-1.0e30)
for (c in 0..3) {
let raw = fcOut.get(0, c).getOrThrow().getValue()
if (raw > maxVal) {
maxVal = raw
}
}
var expSum = Float32(0.0)
for (c in 0..3) {
let raw = fcOut.get(0, c).getOrThrow().getValue()
let ev = Float32(exp(raw - maxVal))
softmax.set(0, c, FP32(ev))
expSum = expSum + ev
}
for (c in 0..3) {
let v = softmax.get(0, c).getOrThrow().getValue()
softmax.set(0, c, FP32(v / expSum))
}
let probVal = softmax.get(0, label).getOrThrow().getValue()
let loss = -log(max(probVal, Float32(1e-10)))
epochLoss = epochLoss + loss
var maxProb = Float32(-1.0)
var predClass = 0
for (c in 0..3) {
let p = softmax.get(0, c).getOrThrow().getValue()
if (p > maxProb) {
maxProb = p
predClass = Int64(c)
}
}
if (predClass == label) {
epochCorrect += 1
}
// ===== 反向传播 =====
var delta = Matrix1(1, 3)
for (c in 0..3) {
let prob = softmax.get(0, c).getOrThrow()
if (Int64(c) == label) {
delta.set(0, c, prob - FP32(1.0))
} else {
delta.set(0, c, prob)
}
}
let fcDelta = fc.backward(delta)
// 调试:确认 fcDelta 形状
if (i == 0 && epoch == 0) {
println("DEBUG: fc.backward done, fcDelta rows: ${fcDelta.get_row()}, cols: ${fcDelta.get_col()}")
}
var flatDelta = Matrix1(8, 1)
for (j in 0..8) {
flatDelta.set(j, 0, fcDelta.get(0, j).getOrThrow())
}
// 调试:确认 flatDelta 形状
if (i == 0 && epoch == 0) {
println("DEBUG: flatDelta rows: ${flatDelta.get_row()}, cols: ${flatDelta.get_col()}")
}
let c2Delta = flatten.backward(flatDelta)
let p1Delta = conv2.backward(c2Delta)
let c1Delta = pool1.backward(p1Delta)
let _ = conv1.backward(c1Delta)
// ===== 参数更新 =====
if ((i + 1) % batchSize == 0) {
// 所有可训练层使用 Adam 更新
adamConv1.updateConvLayer(conv1) // 卷积层1 - Adam
adamConv2.updateConvLayer(conv2) // 卷积层2 - Adam
adamFC.updateFCLayer(fc) // 全连接层 - Adam
// 池化层使用 SGD 更新(如果需要的话)
pool1.update(Float32(1.0))
}
}
// Epoch 结束时的更新
adamConv1.updateConvLayer(conv1)
adamConv2.updateConvLayer(conv2)
adamFC.updateFCLayer(fc)
pool1.update(Float32(1.0))
let avgLoss = epochLoss / Float32(nTrainSamples)
let accuracy = Float32(epochCorrect) / Float32(nTrainSamples) * Float32(100.0)
println("Epoch ${epoch + 1}/${nEpochs} - Loss: ${avgLoss}, Accuracy: ${accuracy}%")
// 打印 Adam 的步数信息
if (epoch == 0) {
println(" Adam Step: ${adamConv1.getStep()}")
}
}
println("")
println("开始测试...")
println("")
var testCorrect = 0
var confusion = Array(3, { _ => Array(3, { _ => 0 }) })
for (i in 0..nTestSamples) {
let x = testInputs[i]
let label = testLabels[i]
let c1Out = conv1.forward(x, (1, 8, 8, 1))
let p1Out = pool1.forward(c1Out, (1, 6, 6, 4))
let c2Out = conv2.forward(p1Out, (1, 3, 3, 4))
let flatOut = flatten.forward(c2Out, (1, 1, 1, 8))
var fcInput = Matrix1(1, 8)
for (j in 0..8) {
fcInput.set(0, j, flatOut.get(j, 0).getOrThrow())
}
let fcOut = fc.forward(fcInput)
var softmax = Matrix1(1, 3)
var maxVal = Float32(-1.0e30)
for (c in 0..3) {
let raw = fcOut.get(0, c).getOrThrow().getValue()
if (raw > maxVal) {
maxVal = raw
}
}
var expSum = Float32(0.0)
for (c in 0..3) {
let raw = fcOut.get(0, c).getOrThrow().getValue()
let ev = Float32(exp(raw - maxVal))
softmax.set(0, c, FP32(ev))
expSum = expSum + ev
}
for (c in 0..3) {
let v = softmax.get(0, c).getOrThrow().getValue()
softmax.set(0, c, FP32(v / expSum))
}
var maxProb = Float32(-1.0)
var predClass = 0
for (c in 0..3) {
let p = softmax.get(0, c).getOrThrow().getValue()
if (p > maxProb) {
maxProb = p
predClass = Int64(c)
}
}
confusion[label][predClass] += 1
if (predClass == label) {
testCorrect += 1
}
}
let testAcc = Float32(testCorrect) / Float32(nTestSamples) * Float32(100.0)
println("测试准确率: ${testAcc}% (${testCorrect}/${nTestSamples})")
print("")
let classNames = ["红色(高)", "绿色(中)", "蓝色(低)"]
println("各类别准确率:")
for (c in 0..3) {
var total = 0
for (p in 0..3) {
total += confusion[c][p]
}
if (total > 0) {
let acc = Float32(confusion[c][c]) / Float32(total) * Float32(100.0)
print(" ${classNames[c]}: ${confusion[c][c]}/${total} (${acc}%)")
}
}
println("")
println("混淆矩阵:")
println(" 预测红 预测绿 预测蓝")
for (t in 0..3) {
var line = "真实${classNames[t]}:"
for (p in 0..3) {
line = line + " ${confusion[t][p]} "
}
print(line)
}
println("")
printSeparator()
println("测试完成!")
printSeparator()
}
+517
View File
@@ -0,0 +1,517 @@
// test_geometry_cnn.cj - 几何图形分类测试(Adam 优化器版本)
package bot4cj
import bot4cj.math.*
import std.math.*
import std.random.*
import std.collection.*
/**
* 计算圆形像素值
*/
func calcCirclePixel(x: Int64, y: Int64, center: Float64, radius: Float64, offsetX: Float64, offsetY: Float64): FP32 {
let dx = Float64(x) - center + offsetX
let dy = Float64(y) - center + offsetY
let dist = sqrt(dx * dx + dy * dy)
if (dist <= radius) {
let intensity = 1.0 - (dist / radius) * 0.3
FP32(Float32(intensity))
} else if (dist <= radius + 1.5) {
let edgeDist = dist - radius
let intensity = max(0.0, 1.0 - edgeDist / 1.5) * 0.7
FP32(Float32(intensity))
} else {
FP32(0.0)
}
}
/**
* 计算正方形像素值
*/
func calcSquarePixel(x: Int64, y: Int64, center: Float64, radius: Float64, offsetX: Float64, offsetY: Float64): FP32 {
let halfSize = radius * 0.8
let left = center - halfSize + offsetX
let right = center + halfSize + offsetX
let top = center - halfSize + offsetY
let bottom = center + halfSize + offsetY
let fx = Float64(x)
let fy = Float64(y)
if (fx >= left && fx <= right && fy >= top && fy <= bottom) {
let distToEdge = min(
min(fx - left, right - fx),
min(fy - top, bottom - fy)
)
let intensity = min(1.0, 0.7 + distToEdge / halfSize * 0.3)
FP32(Float32(intensity))
} else {
let distX = max(0.0, max(left - fx, fx - right))
let distY = max(0.0, max(top - fy, fy - bottom))
let edgeDist = sqrt(distX * distX + distY * distY)
if (edgeDist <= 1.5) {
let intensity = max(0.0, 1.0 - edgeDist / 1.5) * 0.6
FP32(Float32(intensity))
} else {
FP32(0.0)
}
}
}
/**
* 计算三角形像素值
*/
func calcTrianglePixel(x: Int64, y: Int64, center: Float64, radius: Float64, offsetX: Float64, offsetY: Float64): FP32 {
let height = radius * 2.0
let baseWidth = height * 1.2
let topY = center - height * 0.5 + offsetY
let bottomY = center + height * 0.5 + offsetY
let fx = Float64(x)
let fy = Float64(y)
if (fy >= topY && fy <= bottomY) {
let t = (fy - topY) / (bottomY - topY)
let halfWidthAtY = baseWidth * (1.0 - t) * 0.5
let centerX = center + offsetX
if (fx >= centerX - halfWidthAtY && fx <= centerX + halfWidthAtY) {
let distToEdge = min(
fx - (centerX - halfWidthAtY),
(centerX + halfWidthAtY) - fx
)
let intensity = min(1.0, 0.7 + distToEdge / halfWidthAtY * 0.3)
FP32(Float32(intensity))
} else {
let distToLeft = (centerX - halfWidthAtY) - fx
let distToRight = fx - (centerX + halfWidthAtY)
let edgeDist = max(distToLeft, distToRight)
if (edgeDist <= 1.5) {
let intensity = max(0.0, 1.0 - edgeDist / 1.5) * 0.6
FP32(Float32(intensity))
} else {
FP32(0.0)
}
}
} else {
FP32(0.0)
}
}
/**
* 生成几何图形数据
* 图形类型:0=圆形, 1=正方形, 2=三角形
*/
func generateGeometryData(numSamples: Int64): (Array<Matrix1>, Array<Int64>) {
var inputs = Array(numSamples, { _ => Matrix1(1, 1) })
var labels = Array(numSamples, { _ => 0 })
let rng = Random()
let imgSize = 16
for (i in 0..numSamples) {
let shapeType = Int64(rng.nextFloat64() * 3.0)
labels[i] = shapeType
var matrix = Matrix1(imgSize * imgSize, 1)
let center = Float64(imgSize) / 2.0
let radius = Float64(imgSize) / 3.0 + rng.nextFloat64() * 2.0 - 1.0
let offsetX = (rng.nextFloat64() - 0.5) * 2.0
let offsetY = (rng.nextFloat64() - 0.5) * 2.0
for (y in 0..imgSize) {
for (x in 0..imgSize) {
let idx = y * imgSize + x
var pixelValue = match (shapeType) {
case 0 => calcCirclePixel(x, y, center, radius, offsetX, offsetY)
case 1 => calcSquarePixel(x, y, center, radius, offsetX, offsetY)
case _ => calcTrianglePixel(x, y, center, radius, offsetX, offsetY)
}
let noise = FP32(Float32(rng.nextFloat64() - 0.5) * 0.05)
pixelValue = pixelValue + noise
if (pixelValue.getValue() > 1.0) {
pixelValue = FP32(1.0)
} else if (pixelValue.getValue() < 0.0) {
pixelValue = FP32(0.0)
}
matrix.set(idx, 0, pixelValue)
}
}
inputs[i] = matrix
}
(inputs, labels)
}
/**
* 可视化一个样本
*/
func showSample(matrix: Matrix1, imgSize: Int64, label: Int64): Unit {
let shapeNames = ["圆形", "正方形", "三角形"]
println("标签: ${shapeNames[label]}")
for (y in 0..imgSize) {
var line = ""
for (x in 0..imgSize) {
let idx = y * imgSize + x
let val = matrix.get(idx, 0).getOrThrow().getValue()
if (val > 0.8) {
line = line + "██"
} else if (val > 0.6) {
line = line + "▓▓"
} else if (val > 0.4) {
line = line + "▒▒"
} else if (val > 0.2) {
line = line + "░░"
} else {
line = line + " "
}
}
println(line)
}
println("")
}
/**
* Softmax 辅助函数
*/
func computeSoftmax(input: Matrix1): Matrix1 {
let cols = input.get_col()
var output = Matrix1(1, cols)
var maxVal = Float32(-1.0e30)
for (c in 0..cols) {
let raw = input.get(0, c).getOrThrow().getValue()
if (raw > maxVal) {
maxVal = raw
}
}
var expSum = Float32(0.0)
for (c in 0..cols) {
let raw = input.get(0, c).getOrThrow().getValue()
let ev = Float32(exp(raw - maxVal))
output.set(0, c, FP32(ev))
expSum = expSum + ev
}
for (c in 0..cols) {
let v = output.get(0, c).getOrThrow().getValue()
output.set(0, c, FP32(v / expSum))
}
output
}
/**
* 获取预测类别
*/
func predictClass(probs: Matrix1): Int64 {
let cols = probs.get_col()
var maxProb = Float32(-1.0)
var predClass = 0
for (c in 0..cols) {
let p = probs.get(0, c).getOrThrow().getValue()
if (p > maxProb) {
maxProb = p
predClass = Int64(c)
}
}
predClass
}
/**
* 打印分隔线
*/
func printLine(): Unit {
println("============================================================")
}
/**
* 主测试函数
*/
func testGeometryCNN(): Unit {
printLine()
println("CNN 几何图形分类测试 (Adam 优化器版本)")
printLine()
println("")
let imgSize = 16
let nTrainSamples = 200
let nTestSamples = 60
let nEpochs = 50
let batchSize = 10
let nClasses = 3
println("配置信息:")
println(" 图像大小: ${imgSize}x${imgSize}")
println(" 训练样本数: ${nTrainSamples}")
println(" 测试样本数: ${nTestSamples}")
println(" 训练轮数: ${nEpochs}")
println(" Batch 大小: ${batchSize}")
println(" 类别数: ${nClasses} (圆形, 正方形, 三角形)")
println(" 优化器: Adam")
println("")
println("网络结构:")
println(" Input(16x16x1)")
println(" -> Conv1(5x5, 1->8) -> Pool1(2x2)")
println(" -> Conv2(3x3, 8->16) -> Pool2(2x2)")
println(" -> Flatten -> FC(64->32) -> FC(32->3)")
println(" -> Softmax")
println("")
let lr = FP32(0.01)
// 构建网络
var conv1 = ConvLayer(1, 8, (5, 5), 1, 0, lr, Af.Leaky_ReLU(FP32(0.01)), Option.None)
var pool1 = PoolingLayer(8, (2, 2), 2, 0, "max", Af.ReLU, lr, lr)
var conv2 = ConvLayer(8, 16, (3, 3), 1, 0, lr, Af.Leaky_ReLU(FP32(0.01)), Option.None)
var pool2 = PoolingLayer(16, (2, 2), 2, 0, "max", Af.ReLU, lr, lr)
var flatten = FlattenLayer(16, 2, 2)
var fc1 = Nn1(64, 32, Float32(0.3), Af.Leaky_ReLU(FP32(0.01)))
var fc2 = Nn1(32, 3, Float32(0.3), Af.Softmax)
// 创建 Adam 优化器
var adamConv1 = Adam(FP32(0.001)) // 卷积层1
var adamConv2 = Adam(FP32(0.001)) // 卷积层2
var adamFC1 = Adam(FP32(0.0005)) // 全连接层1
var adamFC2 = Adam(FP32(0.0005)) // 全连接层2
println("网络创建完成 (Adam优化器: Conv1, Conv2, FC1, FC2)")
println("")
// 生成数据
println("生成训练数据...")
let (trainInputs, trainLabels) = generateGeometryData(nTrainSamples)
println("生成测试数据...")
let (testInputs, testLabels) = generateGeometryData(nTestSamples)
println("数据生成完成")
println("")
// 显示几个样本
println("样本预览:")
for (i in 0..3) {
if (i < nTrainSamples) {
println("样本 ${i + 1}:")
showSample(trainInputs[i], imgSize, trainLabels[i])
}
}
println("开始训练...")
println("")
var bestAccuracy = Float32(0.0)
// 计算卷积输出尺寸
let conv1OutH = imgSize - 4 // 16 - 5 + 1 = 12
let conv1OutW = imgSize - 4
let pool1OutH = conv1OutH / 2 // 6
let pool1OutW = conv1OutW / 2
let conv2OutH = pool1OutH - 2 // 6 - 3 + 1 = 4
let conv2OutW = pool1OutW - 2
let pool2OutH = conv2OutH / 2 // 2
let pool2OutW = conv2OutW / 2
let flatSize = 16 * pool2OutH * pool2OutW // 64
for (epoch in 0..nEpochs) {
var epochLoss = Float32(0.0)
var epochCorrect = 0
// 训练
for (i in 0..nTrainSamples) {
let x = trainInputs[i]
let label = trainLabels[i]
// 前向传播
let c1Out = conv1.forward(x, (1, imgSize, imgSize, 1))
let p1Out = pool1.forward(c1Out, (1, conv1OutH, conv1OutW, 8))
let c2Out = conv2.forward(p1Out, (1, pool1OutH, pool1OutW, 8))
let p2Out = pool2.forward(c2Out, (1, conv2OutH, conv2OutW, 16))
let flatOut = flatten.forward(p2Out, (1, pool2OutH, pool2OutW, 16))
// 转换为全连接层输入
var fc1Input = Matrix1(1, flatSize)
for (j in 0..flatSize) {
fc1Input.set(0, j, flatOut.get(j, 0).getOrThrow())
}
let fc1Out = fc1.forward(fc1Input)
let fc2Out = fc2.forward(fc1Out)
// Softmax
let probs = computeSoftmax(fc2Out)
// 计算损失和准确率
let probVal = probs.get(0, label).getOrThrow().getValue()
let loss = -log(max(probVal, Float32(1e-10)))
epochLoss = epochLoss + loss
let predClass = predictClass(probs)
if (predClass == label) {
epochCorrect += 1
}
// 反向传播
var delta = Matrix1(1, nClasses)
for (c in 0..nClasses) {
let prob = probs.get(0, c).getOrThrow()
if (Int64(c) == label) {
delta.set(0, c, prob - FP32(1.0))
} else {
delta.set(0, c, prob)
}
}
let fc2Delta = fc2.backward(delta)
let fc1Delta = fc1.backward(fc2Delta)
// 转换回特征图格式
var flatDelta = Matrix1(flatSize, 1)
for (j in 0..flatSize) {
flatDelta.set(j, 0, fc1Delta.get(0, j).getOrThrow())
}
let p2Delta = flatten.backward(flatDelta)
let c2Delta = pool2.backward(p2Delta)
let p1Delta = conv2.backward(c2Delta)
let c1Delta = pool1.backward(p1Delta)
let _ = conv1.backward(c1Delta)
// 批次更新 - 使用 Adam
if ((i + 1) % batchSize == 0) {
adamConv1.updateConvLayer(conv1) // Conv1 - Adam
adamConv2.updateConvLayer(conv2) // Conv2 - Adam
adamFC1.updateFCLayer(fc1) // FC1 - Adam
adamFC2.updateFCLayer(fc2) // FC2 - Adam
// 池化层仍使用 SGD
pool1.update(Float32(1.0))
pool2.update(Float32(1.0))
}
}
// 最终更新 - 使用 Adam
adamConv1.updateConvLayer(conv1)
adamConv2.updateConvLayer(conv2)
adamFC1.updateFCLayer(fc1)
adamFC2.updateFCLayer(fc2)
pool1.update(Float32(1.0))
pool2.update(Float32(1.0))
let avgLoss = epochLoss / Float32(nTrainSamples)
let accuracy = Float32(epochCorrect) / Float32(nTrainSamples) * Float32(100.0)
if (accuracy > bestAccuracy) {
bestAccuracy = accuracy
}
// 输出训练信息
if (epoch % 1 == 0 || epoch == nEpochs - 1) {
println("Epoch ${epoch + 1}/${nEpochs} - Loss: ${avgLoss}, Accuracy: ${accuracy}%, Adam Step: ${adamConv1.getStep()}")
}
}
println("")
println("训练完成! 最佳训练准确率: ${bestAccuracy}%")
println("总更新步数: ${adamConv1.getStep()}")
println("")
// 测试
println("开始测试...")
println("")
var testCorrect = 0
var confusion = Array(nClasses, { _ => Array(nClasses, { _ => 0 }) })
var testLoss = Float32(0.0)
for (i in 0..nTestSamples) {
let x = testInputs[i]
let label = testLabels[i]
// 前向传播
let c1Out = conv1.forward(x, (1, imgSize, imgSize, 1))
let p1Out = pool1.forward(c1Out, (1, conv1OutH, conv1OutW, 8))
let c2Out = conv2.forward(p1Out, (1, pool1OutH, pool1OutW, 8))
let p2Out = pool2.forward(c2Out, (1, conv2OutH, conv2OutW, 16))
let flatOut = flatten.forward(p2Out, (1, pool2OutH, pool2OutW, 16))
var fc1Input = Matrix1(1, flatSize)
for (j in 0..flatSize) {
fc1Input.set(0, j, flatOut.get(j, 0).getOrThrow())
}
let fc1Out = fc1.forward(fc1Input)
let fc2Out = fc2.forward(fc1Out)
let probs = computeSoftmax(fc2Out)
// 计算损失
let probVal = probs.get(0, label).getOrThrow().getValue()
testLoss = testLoss + Float32(-log(max(probVal, Float32(1e-10))))
// 预测
let predClass = predictClass(probs)
confusion[label][predClass] += 1
if (predClass == label) {
testCorrect += 1
}
// 显示前几个错误分类的样本
if (predClass != label && testCorrect < 10) {
println("错误分类样本 ${i + 1}:")
showSample(x, imgSize, label)
let shapeNames = ["圆形", "正方形", "三角形"]
println(" 真实标签: ${shapeNames[label]}, 预测标签: ${shapeNames[predClass]}")
println("")
}
}
let testAcc = Float32(testCorrect) / Float32(nTestSamples) * Float32(100.0)
let avgTestLoss = testLoss / Float32(nTestSamples)
println("测试结果:")
println(" 准确率: ${testAcc}% (${testCorrect}/${nTestSamples})")
println(" 平均损失: ${avgTestLoss}")
println("")
let classNames = ["圆形 ", "正方形", "三角形"]
println("各类别准确率:")
for (c in 0..nClasses) {
var total = 0
for (p in 0..nClasses) {
total += confusion[c][p]
}
if (total > 0) {
let acc = Float32(confusion[c][c]) / Float32(total) * Float32(100.0)
println(" ${classNames[c]}: ${confusion[c][c]}/${total} (${acc}%)")
}
}
println("")
println("混淆矩阵:")
println(" 预测圆形 预测正方形 预测三角形")
for (t in 0..nClasses) {
var line = "真实${classNames[t]}:"
for (p in 0..nClasses) {
line = line + " ${confusion[t][p]} "
}
println(line)
}
println("")
printLine()
println("几何图形分类测试完成!")
printLine()
}