diff --git a/bot4cj/src/P.cj b/bot4cj/src/P.cj index 56f4ada..106dde0 100644 --- a/bot4cj/src/P.cj +++ b/bot4cj/src/P.cj @@ -98,13 +98,13 @@ class MatrixObject { } /* ---------------- 对Nn1进行扩展 ---------------- */ -extend Nn1 { - /* -------- 转换为MatrixObject对象 -------- */ - public func toMto(): MatrixObject { - let mto = MatrixObject() - return mto - } -} +//extend Nn1 { +// /* -------- 转换为MatrixObject对象 -------- */ +// public func toMto(): MatrixObject { +// let mto = MatrixObject() +// return mto +// } +//} /* -------- 矩阵式解析器 -------- * TODO(20260514-qing): 这个类输入流数据,输出MatrixObject对象 diff --git a/bot4cj/src/cpp/I2C.c b/bot4cj/src/cpp/I2C.c deleted file mode 100644 index 5de87c6..0000000 --- a/bot4cj/src/cpp/I2C.c +++ /dev/null @@ -1,152 +0,0 @@ -/* - * i2c - 用户界面 - * - * 这里已经提供了I2C通用接口,可以将上层代码移动到仓颉下面 - */ - -#include -#include -#include -#ifdef __linux__ -#include -#include -#include /* 其中宏的取值可能具有平台特异性 */ -#include -#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, ®, 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, ®, 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 diff --git a/bot4cj/src/cpp/Matrix.cpp b/bot4cj/src/cpp/Matrix.cpp deleted file mode 100644 index cfa0da0..0000000 --- a/bot4cj/src/cpp/Matrix.cpp +++ /dev/null @@ -1,516 +0,0 @@ -#include -#include -#include "Matrix.hh" - -namespace qing { - - template - Matrx::Matrx(): row(0), col(0) {} - - template - Matrx::Matrx(const uint64_t row, const uint64_t col) - : row(row), col(col) { /* 该构造函数用于从头开始构造 */ - arr = std::vector(col * row); - } - - template - Matrx::Matrx(const uint64_t row, const uint64_t col, const std::vector& src) - : row(row), col(col) { /* 该构造函数用于通过已有的vector进行构造 */ - arr = src; - } - - template - const T& Matrx::get(uint64_t row, uint64_t col) const { /* 访问器 - 访问矩阵元素 */ - return arr[col * this->row + row]; - } - - template - T& Matrx::witch(uint64_t row, uint64_t col) { /* 访问器 - 访问矩阵元素 */ - return arr[col * this->row + row]; - } - - template - void Matrx::set(uint64_t row, uint64_t col, T v) { /* 设置矩阵元素 */ - arr[col * this->row + row] = v; - } - - template - const std::vector& Matrx::get_data() const{ /* 访问器 - 用于获取原始数据 */ - return arr; - } - - template - const uint64_t Matrx::get_col() const { /* 获取列数 */ - return col; - } - - template - const uint64_t Matrx::get_row() const { /* 获取行数 */ - return row; - } - - template - bool Matrx::has_same_shape(const Matrx& other) { /* 两个矩阵是否具有相同形状 */ - return other.get_col() == this->get_col() && other.get_row() == this->get_row(); - } - - template - bool Matrx::has_dot_shape(const Matrx& other) { /* 具有矩阵乘法的形状 */ - return this->get_col() == other.get_row(); - } - - template - void Matrx::assert_same_shape(const Matrx& other) { /* 断言两个矩阵具有相同形状 */ - if (!has_same_shape(other)) - throw std::invalid_argument("The shapes of matrix are not same."); - } - - template - void Matrx::assert_dot_shape(const Matrx& other) { /* 断言两个矩阵具有点积前置形状 */ - if (!has_dot_shape(other)) { - throw std::invalid_argument("The shapes of matrix can not be dot."); - } - } - - template - Matrx Matrx::operator+(const Matrx& other) { /* 加法操作符重定义 - 矩阵加法 */ - //assert_same_shape(other); /* 检查两个矩阵形状是否一致 */ - std::vector v0 = this->get_data(); - std::vector 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 - Matrx Matrx::operator+(T& item) { /* 矩阵加法广播 */ - auto len = this->get_data().size(); - auto v0 = std::vector (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 - Matrx Matrx::operator-(const Matrx& other) { /* 操作符重载 - 矩阵减法 */ - //assert_same_shape(other); /* 检查两个矩阵形状是否一致 */ - std::vector v0 = this->get_data(); - std::vector 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 - Matrx Matrx::operator-(T& item) { /* 操作符重载 - 矩阵减法广播 */ - auto len = this->get_data().size(); - auto v0 = std::vector (len, item); - auto matrix = (*this) - Matrx(get_row(), get_col(), v0); - - //Matrx matrix = *this; - //for (auto it = arr.begin(); it != arr.end(); ++it) - // *it -= item; - return matrix; - } - - template - Matrx Matrx::dot(const Matrx& other) { /* 操作符重载 - 矩阵乘法 */ - //assert_dot_shape(other); /* 它们的形状是否能够点积 */ - auto matrix = Matrx(this->get_row(), other.get_col()); - for (uint64_t i=0; iget_row(); ++i) /* 点积运算 */ - for (uint64_t j=0; jget_col(); ++k) - sum += this->get(i, k) * other.get(k, j); - matrix.witch(i, j) = sum; - } - return matrix; - } - - template - Matrx Matrx::operator*(T& item) { /* 操作符重载 - 矩阵乘法广播 */ - std::vector vec = this->get_data(); - for (auto it = vec.begin(); it != vec.end(); ++it) - *it *= item; - return Matrx{this->get_row(), this->get_col(), vec}; - } - - template - Matrx Matrx::operator*(const Matrx& other) { /* 加法操作符重定义 - 矩阵加法 */ - //assert_same_shape(other); /* 检查两个矩阵形状是否一致 */ - std::vector v0 = this->get_data(); - std::vector 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 - Matrx Matrx::operator/(T& item) { /* 操作符重载 - 矩阵除法广播 */ - std::vector vec = this->get_data(); - for (auto it = vec.begin(); it != vec.end(); ++it) - *it /= item; - return Matrx(this->get_row(), this->get_col(), vec); - } - - template - Matrx Matrx::operator/(const Matrx& other) { /* 加法操作符重定义 - 矩阵加法 */ - //assert_same_shape(other); /* 检查两个矩阵形状是否一致 */ - std::vector v0 = this->get_data(); - std::vector 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 class Matrx; /* 显式实例化 */ - template class Matrx; - -} - -/* FIXME: 形状不合法时返回空指针 */ -using namespace qing; -extern "C" { - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* createMatrix(uint64_t row, uint64_t col) { - return (void *) new Matrx(row, col); - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void freeMatrix(void* M) { - delete (Matrx*) M; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - float* witchMatrix(void *m, uint64_t row, uint64_t col) { - Matrx *M = (Matrx*) m; - return &(M->witch(row, col)); - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - float getMatrix(void *m, uint64_t row, uint64_t col) { - Matrx *M = (Matrx*) m; - return M->get(row, col); - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void setMatrix(void *m, uint64_t row, uint64_t col, float v) { - Matrx *M = (Matrx*) m; - M->set(row, col, v); - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - uint64_t getrowMatrix(void *m) { - Matrx *M = (Matrx*) m; - return M->get_row(); - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - uint64_t getcolMatrix(void *m) { - Matrx *M = (Matrx*) m; - return M->get_col(); - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - bool sameShapeMatrix(void *m, void *s) { - Matrx *M = (Matrx*) m; - Matrx *S = (Matrx*) s; - return M->has_same_shape(*S); - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - bool dotShapeMatrix(void *m, void *s) { - Matrx *M = (Matrx*) m; - Matrx *S = (Matrx*) s; - return M->has_dot_shape(*S); - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* plusMatrix(void *m, void *s) { - Matrx *M = (Matrx*) m; - Matrx *S = (Matrx*) s; - auto a = new Matrx((*M) + (*S)); - return (void *) a; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* plusBroadcastMatrix(void *m, float t) { - Matrx *M = (Matrx*) m; - auto a = new Matrx ((*M) + t); - return (void*) a; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* subMatrix(void *m, void *s) { - Matrx *M = (Matrx*) m; - Matrx *S = (Matrx*) s; - auto a = new Matrx((*M) - (*S)); - return (void *) a; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void * subBroadcastMatrix(void *m, float t) { - Matrx *M = (Matrx *) m; - auto a = new Matrx ((*M) - t); - return (void *) a; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* multiMatrix(void *m, void *s) { - Matrx *M = (Matrx*) m; - Matrx *S = (Matrx*) s; - auto a = new Matrx((*M) * (*S)); - return (void *) a; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* multiBroadcastMatrix(void *m, float t) { - Matrx *M = (Matrx *) m; - auto a = new Matrx((*M) * t); - return (void*) a; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* divMatrix(void *m, void *s) { - Matrx *M = (Matrx*) m; - Matrx *S = (Matrx*) s; - auto a = new Matrx((*M) / (*S)); - return (void *) a; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* divBroadcastMatrix(void *m, float t) { - Matrx *M = (Matrx *) m; - auto a = new Matrx((*M) / t); - return (void*) a; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* dotMatrix(void *m, void *s) { - Matrx *M = (Matrx*) m; - Matrx *S = (Matrx*) s; - auto a = new Matrx(M->dot(*S)); - return (void *) a; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* transposeMatrix(void *m) { - Matrx *M = (Matrx *)m; - auto N= new Matrx(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 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 distribution(0.0f, sigma); - - /* 遍历权重矩阵,进行赋值 */ - uint64_t k=0; - for (uint64_t i=0; i(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(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 distribution(-scale, scale); - - /* 遍历权重矩阵,进行赋值 */ - uint64_t k=0; - for (uint64_t i=0; i (fan_in, fan_out, weights); - return (void *)matrix; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* Sigmoid_do(void *m) { - Matrx *M = (Matrx*) m; - std::vector v = M->get_data(); - for (float& x: v) { - x = 1.0 / (1.0 + std::exp(-x)); - } - auto N = new Matrx (M->get_row(), M->get_col(), v); - return (void *)N; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* Sigmoid_dot(void *m) { - Matrx *M = (Matrx*) m; - std::vector v = M->get_data(); - for (float& o: v) { - o = o * (1.0 - o); - } - auto N = new Matrx (M->get_row(), M->get_col(), v); - return (void *)N; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* ReLU_do(void *m) { - Matrx *M = (Matrx*) m; - std::vector v = M->get_data(); - for (float& x: v) { - x = x > 0 ? x : 0; - } - auto N = new Matrx (M->get_row(), M->get_col(), v); - return (void *)N; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* ReLU_dot(void *m) { - Matrx *M = (Matrx*) m; - std::vector v = M->get_data(); - for (float& x: v) { - x = x > 0 ? 1.0f : 0.0f; - } - auto N = new Matrx (M->get_row(), M->get_col(), v); - return (void *)N; - } - - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* Leaky_ReLU_do(void *m, float alpha) { - Matrx *M = (Matrx*) m; - std::vector v = M->get_data(); - for (float& x: v) { - x = x > 0 ? x : alpha * x; - } - auto N = new Matrx (M->get_row(), M->get_col(), v); - return (void *)N; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* Leaky_ReLU_dot(void *m, float alpha) { - Matrx *M = (Matrx*) m; - std::vector v = M->get_data(); - for (float& x: v) { - x = x > 0 ? 1.0 : alpha; - } - auto N = new Matrx (M->get_row(), M->get_col(), v); - return (void *)N; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* Tanh_do(void *m) { - Matrx *M = (Matrx*) m; - std::vector v = M->get_data(); - for (float& x: v) { - x = std::tanh(x); - } - auto N = new Matrx (M->get_row(), M->get_col(), v); - return (void *)N; - } - - #ifdef _WIN64 - _declspec(dllexport) - #endif - void* Tanh_dot(void *m) { - Matrx *M = (Matrx*) m; - std::vector 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 (M->get_row(), M->get_col(), v); - return (void *)N; - } -} diff --git a/bot4cj/src/cpp/Matrix.hh b/bot4cj/src/cpp/Matrix.hh deleted file mode 100644 index b32ced1..0000000 --- a/bot4cj/src/cpp/Matrix.hh +++ /dev/null @@ -1,60 +0,0 @@ -/* - - ___col___ -/ \ -+---------+ \ -| + + + + | | row - 第一维度 -| + + + + | | -+---------+ / - -*/ - -#pragma once -#include -#include -#include -#include -#include -namespace qing { -template -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& 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& get_data() const; - const uint64_t get_col() const; - const uint64_t get_row() const; - bool has_same_shape(const Matrx& other); - bool has_dot_shape(const Matrx& other); - void assert_same_shape(const Matrx& other); - void assert_dot_shape(const Matrx& other); - Matrx operator+(const Matrx& other); - Matrx operator+(T& item); - Matrx operator-(const Matrx& other); - Matrx operator-(T& item); - Matrx operator*(const Matrx& other); - Matrx operator*(T& item); - Matrx operator/(T& item); - Matrx operator/(const Matrx& other); - Matrx dot(const Matrx& other); - - void save(std::ostream& out) { - out << row << " " << col; - for (const auto& item : arr) { - out << " "; - out << item; - } - } - -private: - uint64_t row, col; - std::vector arr; -}; -} \ No newline at end of file diff --git a/bot4cj/src/cpp/bot4cj.h b/bot4cj/src/cpp/bot4cj.h deleted file mode 100644 index 52c098c..0000000 --- a/bot4cj/src/cpp/bot4cj.h +++ /dev/null @@ -1,21 +0,0 @@ -/* 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 diff --git a/bot4cj/src/main.cj b/bot4cj/src/main.cj index dfbda63..82a71c7 100644 --- a/bot4cj/src/main.cj +++ b/bot4cj/src/main.cj @@ -8,16 +8,13 @@ package bot4cj import std.argopt.* import std.collection.* import std.env.* -import std.time.* import std.convert.* -import std.random.* import std.io.* import std.fs.* import stdx.encoding.json.* import bot4cj.hd.* import bot4cj.math.* import ctype4cj.* -import cangjie_tpc::matrix4cj.Matrix /* ---- 神经网络输出路径 ---- */ var NNS_OUT: Option = None @@ -121,69 +118,12 @@ func print_help() func run_test(key: String) { /* - * 神经网络与门测试 - * - * INFO: 我们使用随机生成的动态数据集,这样会导致一些后果 + * 神经网络门测试 */ - if (key == "nn") { - let nn_t = ArrayList() - 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() - } - } + if (key == "nn1") { + testNn() } - else if (key == "nn1") { - - let m = Matrix(1, 2) - let t = Matrix(1, 2) - - //let random = Random() - let random = Random(UInt64(DateTime.now().nanosecond)) - let a = random.nextInt8(2) - let b = random.nextInt8(2) - let t0 = if ((a == 0 && b == 0) || (a ==1 && b == 1)) { 1.0f64 } else { 0.0f64 } - let t1 = if (a != b) { 1.0f64 } else { 0.0f64 } - m.set(0, 0, Float64(a)) - m.set(0, 1, Float64(b)) - t.set(0, 0, t0) - t.set(0, 1, t1) - - let nns = Nns() - nns.add(Nn2(2, 4, 0.01f64, Af.ReLU)) - nns.add(Nn2(4, 2, 0.01f64, Af.ReLU)) - let num = 200 - let prt_num = 10 - for (i in 0..num) { - let loss = nns.train(m, t, 1.0f64) - if (i % prt_num == prt_num-1) { - println("${i}\t: ${loss}") - } - } - } - - - else if (key == "cnn") { - testCNN() - } - - else if (key == "cnn1") { - testGeometryCNN() - } - - /* -------- 测试解析矩阵,并且输出 * -------- */ else if (key == "matrix_o") { diff --git a/bot4cj/src/math/Adam.cj b/bot4cj/src/math/Adam.cj deleted file mode 100644 index a4ebc4b..0000000 --- a/bot4cj/src/math/Adam.cj +++ /dev/null @@ -1,204 +0,0 @@ -// 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 - var v: HashMap - 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() - v = HashMap() - t = 0 - } - - // 默认参数构造函数 - public init() { - learningRate = FP32(0.001) - beta1 = FP32(0.9) - beta2 = FP32(0.999) - epsilon = FP32(0.00000001) - m = HashMap() - v = HashMap() - t = 0 - } - - // 仅自定义学习率的构造函数 - public init(lr: FP32) { - learningRate = lr - beta1 = FP32(0.9) - beta2 = FP32(0.999) - epsilon = FP32(0.00000001) - m = HashMap() - v = HashMap() - 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 - } -} \ No newline at end of file diff --git a/bot4cj/src/math/Af.cj b/bot4cj/src/math/Af.cj index 1d0d4b3..7914678 100644 --- a/bot4cj/src/math/Af.cj +++ b/bot4cj/src/math/Af.cj @@ -1,33 +1,35 @@ package bot4cj.math import std.math.* +import cangjie_tpc::matrix4cj.Matrix -/* -------- Activation Functions 激活函数族 -------- - * TODO(20260516-qing): 增加序列化、反序列化工厂函数 */ +/* + * -------- Activation Functions 激活函数族 -------- + * TODO(20260516-qing): 增加序列化、反序列化工厂函数 + * TODO(20260825-qing): 改为使用matrix4cj库 + */ public enum Af { - None | Sigmoid | ReLU | Leaky_ReLU(FP32) | Tanh | Softmax + None | Sigmoid | ReLU | Leaky_ReLU(Float64) | Tanh /* -------- 执行激活函数 -------- */ - public func forward(x: Matrix1) { + public func forward(x: Matrix) { match(this) { case None => none(x) case Sigmoid => sigmoid(x) case ReLU => relu(x) case Leaky_ReLU(alpha) => leaky_relu(x, alpha) case Tanh => tanh(x) - case Softmax => softmax(x) } } /* -------- 激活函数的导数 -------- */ - public func backward(y: Matrix1) { + public func backward(y: Matrix) { match(this) { case None => none_dot(y) case Sigmoid => sigmoid_dot(y) case ReLU => relu_dot(y) case Leaky_ReLU(alpha) => leaky_relu_dot(y, alpha) case Tanh => tanh_dot(y) - case Softmax => softmax_jacobian(y) } } @@ -39,235 +41,152 @@ public enum Af { case ReLU => "ReLU" case Leaky_ReLU(alpha) => "Leaky_ReLU" case Tanh => "Tanh" - case Softmax => "Softmax" } } - /* */ - public func get_value(): FP32 { + /* NOTE(20260825-qing): 这个函数在序列化时可能会有用 */ + public func get_value(): Float64 { return match(this) { - case None => FP32(0.0f32) - case Sigmoid => FP32(0.0f32) - case ReLU => FP32(0.0f32) + case None => Float64(0.0f64) + case Sigmoid => Float64(0.0f64) + case ReLU => Float64(0.0f64) case Leaky_ReLU(alpha) => alpha - case Tanh => FP32(0.0f32) - case Softmax => FP32(0.0f32) + case Tanh => Float64(0.0f64) + case Softmax => Float64(0.0f64) } } /* -------- 空的激活函数 -------- */ - 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(M: Matrix) { + return M.copy() } /* -------- 空激活函数的导数 -------- */ - 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 + private static func none_dot(M: Matrix) { + let row = M.getRowDimension() + let col = M.getColumnDimension() + return Matrix(row, col, 1.0f64) } /* -------- S型生长曲线 -------- */ - private static func sigmoid(M: Matrix1) { - let row = M.get_row() - let col = M.get_col() - let N = Matrix1(row, col) + private static func sigmoid(M: Matrix) { + let row = M.getRowDimension() + let col = M.getColumnDimension() + let N = Matrix(row, col) for (r in 0..row) { for (c in 0..col) { - let x = M.get(r, c).getOrThrow() - let x1= x.getValue() - let x2 = 1.0f32 / (1.0f32 + exp(-x1)) - N.set(r, c, FP32(x2)) } + let x1 = M.get(r, c) + let x2 = 1.0f64 / (1.0f64 + exp(-x1)) + N.set(r, c, x2) + } } return N } /* -------- S型生长曲线的导数 -------- */ - private static func sigmoid_dot(M: Matrix1) { - let row = M.get_row() - let col = M.get_col() - let N = Matrix1(row, col) + private static func sigmoid_dot(M: Matrix) { + let row = M.getRowDimension() + let col = M.getColumnDimension() + let N = Matrix(row, col) for (r in 0..row) { for (c in 0..col) { - let 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)) } + let x1 = M.get(r, c) + let x2 = 1.0f64 / (1.0f64 + exp(-x1)) + let x3 = x2 * (1.0f64 - x2) + N.set(r, c, x3) + } } return N } /* -------- 线性整流单元 -------- */ - private static func relu(M: Matrix1) { - let row = M.get_row() - let col = M.get_col() - let N = Matrix1(row, col) + private static func relu(M: Matrix) { + let row = M.getRowDimension() + let col = M.getColumnDimension() + let N = Matrix(row, col) for (r in 0..row) { for (c in 0..col) { - let 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)) + let x1 = M.get(r, c) + let x2 =if (x1 > 0.0f64) { x1 } else{ 0.0f64 } + N.set(r, c, x2) } } return N } /* -------- 线性整流单元的导数 -------- */ - private static func relu_dot(M: Matrix1) { - let row = M.get_row() - let col = M.get_col() - let N = Matrix1(row, col) + private static func relu_dot(M: Matrix) { + let row = M.getRowDimension() + let col = M.getColumnDimension() + let N = Matrix(row, col) for (r in 0..row) { for (c in 0..col) { - let 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)) + let x1 = M.get(r, c) + let x2 =if (x1 > 0.0f64) { 1.0f64 } else{ 0.0f64 } + N.set(r, c, x2) } } return N } /* -------- 带泄露的线性整流单元 -------- */ - private static func leaky_relu(M: Matrix1, alpha: FP32) { - let row = M.get_row() - let col = M.get_col() - let N = Matrix1(row, col) + private static func leaky_relu(M: Matrix, alpha: Float64) { + let row = M.getRowDimension() + let col = M.getColumnDimension() + let N = Matrix(row, col) for (r in 0..row) { for (c in 0..col) { - let 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)) + let x1 = M.get(r, c) + let x2 =if (x1 > 0.0f64) { x1 } else{ x1 * alpha } + N.set(r, c, x2) } } return N } /* -------- 带泄露的整流单元导数 -------- */ - private static func leaky_relu_dot(M: Matrix1, alpha: FP32) { - let row = M.get_row() - let col = M.get_col() - let N = Matrix1(row, col) + private static func leaky_relu_dot(M: Matrix, alpha: Float64) { + let row = M.getRowDimension() + let col = M.getColumnDimension() + let N = Matrix(row, col) for (r in 0..row) { for (c in 0..col) { - let 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)) } + let x1 = M.get(r, c) + let x2 =if (x1 > 0.0f64) { 1.0f64 } else{ alpha } + N.set(r, c, x2) + } } return N } /* -------- 双曲正切函数 --------*/ - private static func tanh(M: Matrix1) { - let row = M.get_row() - let col = M.get_col() - let N = Matrix1(row, col) + private static func tanh(M: Matrix) { + let row = M.getRowDimension() + let col = M.getColumnDimension() + let N = Matrix(row, col) for (r in 0..row) { for (c in 0..col) { - let x = M.get(r, c).getOrThrow() - let x1= x.getValue() + let x1 = M.get(r, c) let x2 = tanh(x1) - N.set(r, c, FP32(x2)) } + N.set(r, c, 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) + private static func tanh_dot(M: Matrix) { + let row = M.getRowDimension() + let col = M.getColumnDimension() + let N = Matrix(row, col) for (r in 0..row) { for (c in 0..col) { - let y = 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)) + let y_val = M.get(r, c) + let rea = 1.0f64 - y_val * y_val + N.set(r, c, rea) } } 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 - } - } diff --git a/bot4cj/src/math/Ini.cj b/bot4cj/src/math/Ini.cj index e151697..e0a5e0c 100644 --- a/bot4cj/src/math/Ini.cj +++ b/bot4cj/src/math/Ini.cj @@ -8,19 +8,6 @@ import std.math.* * - 这样在矩阵式中可以定义初始化方式 */ 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)) - } - } - } - /* xavier正态初始化 - 返回数组 */ public static func xavierNormalInit(fan_in: Int64, fan_out: Int64): Array> { let seed = DateTime.now().nanosecond @@ -30,20 +17,6 @@ public class Ini { return array } - /* -------- 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)) } - } - - } - /* xavier正态初始化 - 返回数组 */ public static func xavierUniformInit(fan_in: Int64, fan_out: Int64): Array> { let limit = sqrt(6.0f64 / Float64(fan_in + fan_out)) diff --git a/bot4cj/src/math/Matrix.cj b/bot4cj/src/math/Matrix.cj deleted file mode 100644 index 62e34c6..0000000 --- a/bot4cj/src/math/Matrix.cj +++ /dev/null @@ -1,535 +0,0 @@ -package bot4cj.math -import std.fs.* -import std.io.* -import std.convert.* -//foreign func createMatrix(row: UInt64, col: UInt64): CPointer -//foreign func freeMatrix(M: CPointer): Unit -//foreign func witchMatrix(M: CPointer, row: UInt64, col: UInt64): CPointer -//foreign func getMatrix(M: CPointer, row: UInt64, col: UInt64): Float32 -//foreign func setMatrix(M: CPointer, row: UInt64, col: UInt64, v: Float32): Unit -//foreign func getrowMatrix(M: CPointer): UInt64 -//foreign func getcolMatrix(M: CPointer): UInt64 -//foreign func sameShapeMatrix(M: CPointer, S: CPointer): Bool -//foreign func dotShapeMatrix(M: CPointer, S: CPointer): Bool -//foreign func plusMatrix(M: CPointer, S:CPointer): CPointer -//foreign func plusBroadcastMatrix(M: CPointer, T: Float32): CPointer -//foreign func subMatrix(M: CPointer, S: CPointer): CPointer -//foreign func subBroadcastMatrix(M: CPointer, T: Float32): CPointer -//foreign func multiMatrix(M: CPointer, S: CPointer): CPointer -//foreign func multiBroadcastMatrix(M: CPointer, T: Float32): CPointer -//foreign func divMatrix(M: CPointer, S: CPointer): CPointer -//foreign func divBroadcastMatrix(M: CPointer, T: Float32): CPointer -//foreign func dotMatrix(M: CPointer, S: CPointer): CPointer -//foreign func transposeMatrix(M: CPointer): CPointer -//foreign func xavierNormalInit(fan_in: UInt64, fan_out: UInt64): CPointer -//foreign func xavierUniformInit(fan_in: UInt64, fan_out: UInt64): CPointer - - -/* -------- 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 /* ---- 数据 ---- */ - - /* -------- 构造器,默认值为0 -------- */ - public Matrix1(row: Int64, col: Int64) { - this.row = row - this.col = col - this.arr = Array(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) { 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 { -// // 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 { 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) { -// let s = sr.readUntil({r => r==r' ' || r ==r'\n'}) -// return Float32.parse(s.getOrThrow()) -// } -// -// /* -------- 静态方法,加载矩阵 -------- */ -// public static func load(sr: StringReader, 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 -//} diff --git a/bot4cj/src/math/Tensor.cj b/bot4cj/src/math/Tensor.cj deleted file mode 100644 index 037f847..0000000 --- a/bot4cj/src/math/Tensor.cj +++ /dev/null @@ -1,56 +0,0 @@ -package bot4cj.math -import std.collection.* - -/* -------- 张量 -------- - * INFO(20260524-qing): 输入一个维度列表 */ -class Tensor { - private let data: Array - private let dims: Array - private let size: Int64 - public init(dims: Array, val: T) { - var size = 1 - for (item in dims) { /* ---- 计算总量 ---- */ - size *= item - } - this.data = Array(size, {_ => val}) - this.dims = dims - this.size = size - } - - /* -------- 私有构造函数,用于内部创建张量 -------- */ - private init(dims: Array, data: Array) { - this.dims = dims - this.data = data - var size = 1 - for (item in dims) { - size *= item - } - this.size = size - } - - /* -------- 获取维度列表(形状) -------- */ - public func shape(): Array { - return dims - } - - /* -------- 逐元素进行某种运算 -------- */ - public func oneByOne(S: Tensor, f: (T, T) -> T): Tensor { - 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(size, {i => f(data[i], S.data[i])}) - return Tensor(dims, res_data) - } -} diff --git a/bot4cj/src/math/cnn.cj b/bot4cj/src/math/cnn.cj deleted file mode 100644 index f0b50ee..0000000 --- a/bot4cj/src/math/cnn.cj +++ /dev/null @@ -1,379 +0,0 @@ -// 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 - var biases: Matrix1 - - var prevInput: Option = Option.None - var prevInputShape: Option<(Int64, Int64, Int64, Int64)> = Option.None - var prevCombination: Option = Option.None - var prevOutput: Option = Option.None - var prevOutputShape: Option<(Int64, Int64, Int64, Int64)> = Option.None - - var kernelsGrad: Option> = Option.None - var biasGrad: Option = 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) - } -} diff --git a/bot4cj/src/math/fft.cj b/bot4cj/src/math/fft.cj deleted file mode 100644 index abcbd9d..0000000 --- a/bot4cj/src/math/fft.cj +++ /dev/null @@ -1,11 +0,0 @@ -package bot4cj.math -//foreign func start_fft(M: CPointer): CPointer -//foreign func gen_wave(): CPointer -/*-------- 生成测试用的正弦波 --------*/ -//public func genWave(): Matrix { -// unsafe { return Matrix(gen_wave()) } -//} -/*-------- 执行fft算法 --------*/ -//public func start_fft(M: Matrix): Matrix { -// unsafe { return Matrix(start_fft(M.getData())) } -//} diff --git a/bot4cj/src/math/flatten.cj b/bot4cj/src/math/flatten.cj deleted file mode 100644 index aa97beb..0000000 --- a/bot4cj/src/math/flatten.cj +++ /dev/null @@ -1,136 +0,0 @@ -// 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 - var prevInputShape: Option<(Int64, Int64, Int64, Int64)> - var prevOutput: Option - 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 - } -} diff --git a/bot4cj/src/math/nn.cj b/bot4cj/src/math/nn.cj index fc7f243..33bd0d0 100644 --- a/bot4cj/src/math/nn.cj +++ b/bot4cj/src/math/nn.cj @@ -1,25 +1,18 @@ package bot4cj.math +import std.time.* import std.fs.* import std.io.* +import std.random.* import std.convert.* import std.collection.* import cangjie_tpc::matrix4cj.Matrix -//foreign func Sigmoid_do(m: CPointer): CPointer -//foreign func Sigmoid_dot(m: CPointer): CPointer -//foreign func ReLU_do(m: CPointer): CPointer -//foreign func ReLU_dot(m: CPointer): CPointer -//foreign func Leaky_ReLU_do(m: CPointer, alpha: Float32): CPointer -//foreign func Leaky_ReLU_dot(m: CPointer, alpha: Float32): CPointer -//foreign func Tanh_do(m: CPointer): CPointer -//foreign func Tanh_dot(m: CPointer): CPointer - public class Nns { - let nns = ArrayList() + let nns = ArrayList() - public func add(layer: Nn2) { + public func add(layer: Nn) { nns.add(layer) } @@ -71,7 +64,7 @@ public class Nns { * TODO: 1.使用matrix4cj重写 * 2.使用数组表示批次 */ -public class Nn2 { +public class Nn { /* ---- 形状 ---- */ let inputno: Int64 @@ -91,35 +84,6 @@ public class Nn2 { var weights_grad: Option = None var bias_grad: Option = None - /* -------- 带泄露的线性整流单元 -------- */ - private static func leaky_relu(M: Matrix, alpha: Float64) { - let row = M.getRowDimension() - let col = M.getColumnDimension() - let N = Matrix(row, col) - for (r in 0..row) { - for (c in 0..col) { - let x1 = M.get(r, c) - let x2 =if (x1 > 0.0f64) {x1} else{x1 * alpha} - N.set(r, c, x2) - } - } - return N - } - - /* -------- 带泄露的整流单元导数 -------- */ - private static func leaky_relu_dot(M: Matrix, alpha: Float64) { - let row = M.getRowDimension() - let col = M.getColumnDimension() - let N = Matrix(row, col) - for (r in 0..row) { - for (c in 0..col) { - let x1 = M.get(r, c) - let x2 =if (x1 > 0.0f64) { 1.0f64 } else{ alpha } - N.set(r, c, x2) - } - } - return N - } /* ---- 初始化器 ---- */ public init(i: Int64, o: Int64, lr: Float64, f: Af) { inputno = i @@ -136,14 +100,14 @@ public class Nn2 { var r = x.times(weights) r = r.plus(bias) prev_raw_output = r - var r1 = leaky_relu(r, 0.01f64) + var r1 = f.forward(r) prev_output = r1 return r1 } /* ---- 反向传播 ---- */ public func backward(e: Matrix) { - let fb = leaky_relu_dot(prev_raw_output.getOrThrow(), 0.01f64) + let fb = f.backward(prev_raw_output.getOrThrow()) let first = e.arrayTimes(fb) let mx = prev_input.getOrThrow().transpose() weights_grad = mx.times(first) @@ -161,320 +125,30 @@ public class Nn2 { } -/* -------- 新版的神经网络,使用原生矩阵对象实现 -------- - * TODO(20260517-qing): 增加batch批次处理,使用ArrayList - * TODO*(20260519-qing): 将神经网络转换为MatrixObject矩阵式对象 - * TODO(20260524-qing): 增加Adam优化器 */ +public func testNn() { + let m = Matrix(1, 2) + let t = Matrix(1, 2) -public class Nn1 { + //let random = Random() + let random = Random(UInt64(DateTime.now().nanosecond)) + let a = random.nextInt8(2) + let b = random.nextInt8(2) + let t0 = if ((a == 0 && b == 0) || (a ==1 && b == 1)) { 1.0f64 } else { 0.0f64 } + let t1 = if (a != b) { 1.0f64 } else { 0.0f64 } + m.set(0, 0, Float64(a)) + m.set(0, 1, Float64(b)) + t.set(0, 0, t0) + t.set(0, 1, t1) - let 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 = None /* ---- 之前的输入 ---- */ - var prev_raw_inputs: Option = None /* ---- 之前的原始输出 ---- */ - var prev_outputs: Option = None /* ---- 之前的输出 ---- */ - /*------------------*/ - var weights_grad: Option = None /* ---- 权重梯度 ---- */ - var bias_grad: Option = 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 + let nns = Nns() + nns.add(Nn(2, 4, 0.01f64, Af.Leaky_ReLU(0.1f64))) + nns.add(Nn(4, 2, 0.01f64, Af.Sigmoid)) + let num = 1000 + let prt_num = 100 + for (i in 0..num) { + let loss = nns.train(m, t, 1.0f64) + if (i % prt_num == prt_num-1) { + println("${i}\t: ${loss}") } } - - 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(sr: StringReader) where T <: Parsable -// { -// 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) -// { -// let inputno = loadItem(sr) -// let outputno = loadItem(sr) -// let lr = loadItem(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 -//} diff --git a/bot4cj/src/math/nns.cj b/bot4cj/src/math/nns.cj deleted file mode 100644 index fd4a65b..0000000 --- a/bot4cj/src/math/nns.cj +++ /dev/null @@ -1,164 +0,0 @@ -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, 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, 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 { -// let nns = ArrayList() -// let file = File(filename, OpenMode.Read) -// let sr = StringReader(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, 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, 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, m: Matrix, t: Matrix, discount: Float32) { -// -// /* ----------------- */ -// let outs_dof = ArrayList () -// let outs_dotf = ArrayList () -// -// /* -------- 正向传播 -------- */ -// 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) -// } -// -//} diff --git a/bot4cj/src/math/pooling.cj b/bot4cj/src/math/pooling.cj deleted file mode 100644 index 7b60c51..0000000 --- a/bot4cj/src/math/pooling.cj +++ /dev/null @@ -1,418 +0,0 @@ -// 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 = Option.None - var prevInputShape: Option<(Int64, Int64, Int64, Int64)> = Option.None - var prevCombination: Option = Option.None - var prevOutput: Option = Option.None - var prevOutputShape: Option<(Int64, Int64, Int64, Int64)> = Option.None - var prevMaxIndices: Option = Option.None - - var weightsGrad: Option = Option.None - var biasGrad: Option = 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) - } -} diff --git a/bot4cj/src/testCNN.cj b/bot4cj/src/testCNN.cj deleted file mode 100644 index 8b91fef..0000000 --- a/bot4cj/src/testCNN.cj +++ /dev/null @@ -1,310 +0,0 @@ -// test_cnn.cj - 使用 Adam 优化器的版本(所有层) -package bot4cj -import bot4cj.math.* -import std.math.* -import std.random.* -import std.collection.* - -func generateSolidColorData(numSamples: Int64): (Array, Array) { - 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() -} \ No newline at end of file diff --git a/bot4cj/src/testCNN1.cj b/bot4cj/src/testCNN1.cj deleted file mode 100644 index 0b4fe8c..0000000 --- a/bot4cj/src/testCNN1.cj +++ /dev/null @@ -1,517 +0,0 @@ -// 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, Array) { - 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() -} \ No newline at end of file