遗漏的部分

This commit is contained in:
qing committed 2026-08-02 21:21:03 +08:00
1 parent 84fb370da8
commit 0493383ee8
63 files changed
+28237 -4

No files matched your search

-4
View File
@@ -4,10 +4,6 @@ users/
*.pem
build/
build_*/
nn
camera
lci
pmc
# ide
.vscode/
+7
View File
@@ -0,0 +1,7 @@
from .to_pmc import PmcComm
from .to_dnfs import DnfsComm
from .from_cops import CopsComm
from .rsa import Rsa_Pri_Key
from .aes import MyAES
from .parser import upload_file
#from .chat_comm import print_to_pmc
+85
View File
@@ -0,0 +1,85 @@
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
class MyAES:
def __init__(self, key: str, iv: str):
self.key: bytes = bytes.fromhex(key)
self.iv: bytes = bytes.fromhex(iv)
def decrypt(self, ciphertext: str):
cipher: bytes = bytes.fromhex(ciphertext)
plain: bytes = MyAES.aes_decrypt(cipher, self.key, self.iv)
try:
return plain.decode('utf-8')
except UnicodeDecodeError as e:
# 返回原始字节的十六进制表示作为备选
return plain.hex()
def encrypt(self, original_text: str):
plaintext: byte = original_text.encode('utf-8')
ciphertext: byte = MyAES.aes_encrypt(plaintext, self.key, self.iv)
return ciphertext.hex()
@staticmethod
def aes_decrypt(ciphertext: bytes, key: bytes, iv: bytes) -> bytes:
"""
使用AES-256-CBC算法解密数据
参数
- ciphertext: 待解密的密文字节串
- key: 256位(32字节)的密钥字节串
- iv: 初始化向量字节串(必须为16字节)
返回:
- 解密后的明文字节串
"""
# 验证输入参数长度
if len(key) != 32:
raise ValueError('AES-256密钥必须是32字节长度')
if len(iv) != 16:
raise ValueError('AES-CBC的IV必须是16字节长度')
# 创建AES-CBC解密器
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
try:
# 执行解密并移除PKCS#7填充
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
return plaintext
except Exception as e:
print(f"解密失败:{e}")
return b''
@staticmethod
def aes_encrypt(plaintext: bytes, key: bytes, iv: bytes) -> bytes:
"""
AES-256-CBC 加密
参数
plaintext: 要加密的密文
key: 32字节的密钥
iv: 16字节的初始化向量
返回:
加密后的密文
"""
# 检查密钥和IV的长度
if len(key) != 32:
raise ValueError("密钥必须是32字节(AES-256)")
if len(iv) != 16:
raise ValueError("IV必须是16字节")
# 创建加密器
cipher = Cipher(
algorithms.AES(key),
modes.CBC(iv),
backend=default_backend()
)
encryptor = cipher.encryptor()
# 添加PKCS7填充
padder = padding.PKCS7(128).padder()
padded_data = padder.update(plaintext) + padder.finalize()
# 加密数据
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
return ciphertext
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env python3
import argparse
import socket
def send_client(msg: str, addr: str, port: int, name: str):
'''发送消息给聊天室服务器。
需要输入消息、地址、端口、昵称'''
try:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((addr, port))
s.sendall(f"{name}: {msg}".encode('utf-8'))
s.close()
print('success')
except ConnectionRefusedError:
print(f"Could not connect to {addr}:{port}")
except Exception as e:
print(f"Unexpected error: {e}")
class ChatComm:
'''与聊天服务器进行交流的类。'''
def __init__(self, addr: str, port: int, name: str):
self.addr = addr
self.port = port
self. name = name
def send(self, msg: str):
send_client(msg, self.addr, self.port, self.name)
def main():
parser = argparse.ArgumentParser(description='发送到聊天室。')
parser.add_argument("--msg", type=str, help="要发送的消息")
parser.add_argument('--addr', type=str, default='localhost', help='选择要连接的地址')
parser.add_argument('--port', type=int, default=8003, help='选择要连接的地址')
parser.add_argument('--name', type=str, default='Unknow', help='你的名字是什么')
args = parser.parse_args()
comm = ChatComm(args.addr, args.port, args.name)
comm.send(args.msg)
if __name__ == '__main__':
main()
+25
View File
@@ -0,0 +1,25 @@
#!/usr/bin/env python3
'''
这是用于访问协处理器http服务的程序
'''
import requests
import os
import argparse
class CopsComm:
'''与协处理器的通信类'''
def __init__(self, url: str):
self.url: str= url
def http_post(self, module: str):
response = requests.post(self.url, json={'cmd': module}, headers={'Content-Type': 'application/json'} )
response.raise_for_status()
return response.text
if __name__ == "__main__":
# python3 from_cops.py --url=http://192.168.254.101:8001/api --mod=MPU6050
parser = argparse.ArgumentParser(description="COPS的http调用示例。")
parser.add_argument('--url', help='访问的URL')
parser.add_argument('--mod', help='要输入的指令')
args = parser.parse_args()
comm = CopsComm(args.url)
rsp = comm.http_post(args.mod)
print(rsp)
+99
View File
@@ -0,0 +1,99 @@
import urllib.request
import argparse
import sys
def openfile(filename):
try:
with open(filename, "r", encoding='utf-8') as f:
content = f.read()
print(f"✅ 文件读取成功,内容长度: {len(content)} 字符")
return content
except FileNotFoundError as e:
print(f"❌ 错误:指定的文件不存在 -> {filename}")
raise e
except PermissionError as e:
print(f"❌ 错误:没有权限读取文件 -> {filename}")
raise e
except Exception as e:
print(f"❌ 错误:读取文件失败 -> {str(e)}")
raise e
def httppost(url: str, msg: str) -> str:
try:
# 核心转换:字符串 → 字节流(原生库必须要求)
post_body_bytes = msg.encode("utf-8")
# 发送POST请求,timeout=10秒防止卡死
response = urllib.request.urlopen(url, data=post_body_bytes, timeout=2)
# 解析响应结果
if response.getcode() == 200:
resp_content = response.read().decode("utf-8")
print("\n🎉 请求上传成功!HTTP状态码: 200")
print(f"🎉 服务端响应内容:\n{resp_content}")
else:
resp_content = f'---{str(response.getcode())}---'
print(f"\n⚠️ 请求上传完成,但服务端返回异常状态码: {response.getcode()}")
print(f"⚠️ 服务端响应内容: {resp_content}")
return resp_content
except urllib.error.URLError as e:
print(f"\n❌ 请求失败:网络错误/连接超时/端口未开放 -> {str(e)}")
raise e
except urllib.error.HTTPError as e:
print(f"\n❌ 请求失败:HTTP错误 -> 状态码: {e.code}, 详情: {e.read().decode('utf-8')}")
raise e
except Exception as e:
print(f"\n❌ 请求失败:未知错误 -> {str(e)}")
raise e
def upload_file(file: str, host: str, port: int) -> str:
"""抽象的URL访问函数,用于上传文件到指定服务器
Args:
file: 待上传的文件路径
host: 目标服务器IP
port: 目标服务器端口
"""
try:
# 读取文件内容
msg = openfile(file)
# 构造URL
url = f"http://{host}:{port}/api/v1/mybot-sample"
# 发送POST请求
resp = httppost(url, msg)
except Exception as e:
print(f"❌ 错误:上传文件失败 -> {str(e)}")
resp = str(e)
return resp
def upload_file_and_train(file: str, host: str, port: int) -> str:
"""抽象的URL访问函数,用于上传文件到指定服务器
Args:
file: 待上传的文件路径
host: 目标服务器IP
port: 目标服务器端口
"""
try:
# 读取文件内容
msg = openfile(file)
# 构造URL
url = f"http://{host}:{port}/api/v1/mybot-nn"
# 发送POST请求
resp = httppost(url, msg)
except Exception as e:
print(f"❌ 错误:上传文件失败 -> {str(e)}")
resp = str(e)
return resp
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="python 远程上传文本内容")
parser.add_argument("file", type=str, help="待上传的文件")
parser.add_argument('--port', type=int, default=9203, help="目标服务器监听端口,默认9203")
parser.add_argument("--host", type=str, default="127.0.0.1", help="目标服务器的IP,默认本地")
args =parser.parse_args()
resp = upload_file(args.file, args.host, args.port)
#resp = upload_file_and_train(args.file, args.host, args.port)
print(resp)
+103
View File
@@ -0,0 +1,103 @@
from cryptography.hazmat.primitives import serialization, hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend
import os
class Rsa_Pri_Key:
'''RSA 私钥对象'''
def __init__(self, fullpath: str) -> None:
self.pri_key=Rsa_Pri_Key.load_private_key(fullpath)
def decrypt(self, ciphertext: str) -> str:
return Rsa_Pri_Key.decrypt_hex_ciphertext(self.pri_key, ciphertext)
@staticmethod
def load_private_key(pem_file, password=None):
'''
从PEM文件加载RSA私钥
:param pem_file: PEM格式的私钥文件路径
:param password: 私钥密码(如果有)
:return RSA私钥对象
'''
with open(pem_file, 'rb') as key_file:
private_key = serialization.load_pem_private_key(
key_file.read(),
password=password.encode() if password else None,
backend=default_backend()
)
return private_key
@staticmethod
def decrypt_hex_ciphertext(private_key, hex_ciphertext):
'''
解密HEX格式的RSA密文
:param private_key: 已加载的RSA私钥
:param hex_ciphertext: Hex字符串格式的密文
:return: 解密后的明文
'''
try:
# 将Hex字符串转换为bytes
ciphertext = bytes.fromhex(hex_ciphertext)
# 使用OAEP填充方案解密
plaintext = private_key.decrypt(
ciphertext,
padding.OAEP(
# OPENSSL 没有显式指定时,这个是SHA1
mgf=padding.MGF1(algorithm=hashes.SHA1()),
algorithm=hashes.SHA1(),
label=None
)
)
return plaintext
except ValueError as e:
print(f"Hex解码失败:{str(e)}")
return None
except Exception as e:
print(f"解密失败:{str(e)}")
return None
def main():
...
if __name__ == '__main__':
main()
"""
# 生成RSA私钥
private_key = rsa.generate_private_key(
public_exponent=65537, # 通常使用65537作为公钥指数
key_size=2048, # 密钥长度,推荐至少2048位
)
# 将私钥序列化为PEM格式
pem_private_key = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption() # 这里不加密私钥
)
# 获取对应的公钥
public_key = private_key.public_key()
# 将公钥序列化为PEM格式
pem_public_key = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo
)
# 打印密钥
print("私钥:")
print(pem_private_key.decode('utf-8'))
print("公钥:")
print(pem_public_key.decode('utf-8'))
# 可选:将密钥保存到文件
with open('private_key.pem', 'wb') as f:
f.write(pem_private_key)
with open('public_key.pem', 'wb') as f:
f.write(pem_public_key)
"""
+118
View File
@@ -0,0 +1,118 @@
import requests
import hashlib
import json
def calculate_sha256(input_string):
"""
计算输入字符串的SHA-256哈希值
参数:
input_string (str): 要计算哈希的字符串
返回:
str: 输入字符串的SHA-256哈希值(十六进制字符串)
"""
# 创建sha256哈希对象
sha256_hash = hashlib.sha256()
# 更新哈希对象,需要将字符串编码为字节
sha256_hash.update(input_string.encode('utf-8'))
# 获取十六进制格式的哈希值
hex_digest = sha256_hash.hexdigest()
return hex_digest
class DnfsComm:
'''=====抛出异常表示失败。=====
Dnfs即树状网络文件系统,该类是用于与dnfs通信的类。
可以获取子节点列表、添加节点、删除节点、更新节点。
计划在以后加入分组获取内容的功能。'''
def __init__(self, url: str) -> None:
self.url: str = url
# 获取某个节点下的子节点(0是主节点)
def get(self, pid: int) -> object:
rsp = requests.post(self.url + "/get_notes", json={
"pid": pid
},)
if (rsp.status_code == 200):
return rsp.text
else:
raise DnfsComm.GetFailed(rsp.text)
def get_and_trans(self, pid: int) -> list:
return json.loads(self.get(pid))
# 以pid节点为父节点添加节点
def add(self, pid: int, title: str, content: str) -> object:
rsp = requests.post(self.url + "/add_note", json={
"pid": pid,
"title": title,
"content": content
},)
if (rsp.status_code == 200 or rsp.status_code == 201):
return rsp.text
else:
raise DnfsComm.AddFailed(rsp.text)
# 删除一个节点
def dele(self, id: int) -> object:
rsp = requests.post(self.url + "/delete_note", json={
"id": id,
},)
if (rsp.status_code == 200):
return rsp.text
else:
raise DnfsComm.DelFailed(rsp.text)
# 更新id节点
def upd(self, id: int, pid: int, title: str, content: str) -> object:
rsp = requests.post(self.url + "/update_note", json={
"id": id,
"pid": pid,
"title": title,
"content": content
},)
if (rsp.status_code == 200):
return rsp.text
else:
raise DnfsComm.UpdFailed(rsp.text)
class TheExp(Exception):
def __init__(self, msg: str):
super().__init__(msg)
class GetFailed(TheExp):
def __init__(self, msg: str):
super().__init__(msg)
class AddFailed(TheExp):
def __init__(self, msg: str):
super().__init__(msg)
class DelFailed(TheExp):
def __init__(self, msg: str):
super().__init__(msg)
class UpdFailed(TheExp):
def __init__(self, msg):
super().__init__(msg)
if __name__ == "__main__":
comm = DnfsComm("http://101.245.108.250:8006")
#comm.add(0, "test pkg", "hello")
#comm.dele(2)
#comm.upd(id=1, pid=0, title="Users", content=f"""1::admin::{calculate_sha256('baigezi')}::admin
#2::qing::{calculate_sha256('123456')}::normal
#3::test::{calculate_sha256('44112233')}::normal
#4::test2::{calculate_sha256('44112233')}::mormal""")
#comm.upd(id=2, pid=0, title="Sensors", content='''101::temperature
#102::humidity''')
comm.upd(id=3, pid=0, title="Protected", content='''''')
print(comm.get_and_trans(0))
+135
View File
@@ -0,0 +1,135 @@
#!/usr/bin/env python3
'''
这个代码是与PMC服务通信的工具
即PMC子系统调用
通过PMC服务,你可以创建和管理进程
===============================
20250701: 版本:0.0.4
使得这个模块可以访问任何地址的pmc服务
20250702:版本 0.0.5
使得这个模块在创建之初就保存一个url
20250709: 版本 0.1.0
使得模块在通信过程中应用RSA和AES加密手段
'''
from .rsa import Rsa_Pri_Key
from .aes import MyAES
import requests
import os
import json
import sys
class PmcComm:
'''并发机器通信'''
def __init__(self, url: str):
self.url: str = url
# 新库
############################
@staticmethod
def post_to(url, payload="", retry=10):
count = 0
while count < retry:
count += 1
try:
response = requests.post(url, data=payload)
response.raise_for_status()
return response.text
except requests.exceptions.HTTPError:
...
raise Exception("连接失败")
@staticmethod
def get_from(url, retry=10):
count = 0
while count < retry:
count += 1
try:
response = requests.get(url)
response.raise_for_status()
return response.text
except requests.exceptions.HTTPError:
...
raise Exception("连接失败")
def get_key(self) -> str:
return PmcComm.get_from(f"{self.url}/api/v1/get_key")
def get_iv(self) -> str:
return PmcComm.get_from(f"{self.url}/api/v1/get_iv")
def get_list(self,) -> str:
ls = PmcComm.get_from(f"{self.url}/api/v1/get_list")
return self.aes.decrypt(ls).strip() # 第二天早上就出现了空格
def start(self, module: str) -> str:
msg = json.dumps({'module': module})
msg = self.aes.encrypt(msg)
return PmcComm.post_to(
f'{self.url}/api/v1/start',
payload=msg
)
def try_start(self, module: str) -> str:
try:
self.start(module)
except Exception as e:
print(f'{module}: {e}', file=sys.stderr)
def kill(self, pid: int) -> str:
msg = json.dumps({'pid': pid})
msg = self.aes.encrypt(msg)
return PmcComm.post_to(
f'{self.url}/api/v1/kill',
payload=msg
)
def clear(self, ) -> str:
return PmcComm.post_to(f'{self.url}/api/v1/clear')
def list_mods(self,) -> str:
ls = PmcComm.get_from(f"{self.url}/api/v1/list_mods")
return self.aes.decrypt(ls)
def Set_RSA_Pri_Key(self, fullpath: str) -> None:
self.rsa_pri: Rsa_Pro_Key = Rsa_Pri_Key(fullpath)
def Set_AES_Key(self, key: str, iv: str) -> None:
self.aes = MyAES(key.decode(), iv.decode())
def Try_Get_AES_Key(self, retry: int=10) -> None:
'''试图获取AES密钥,测试中失败率比较高'''
key_data = self.get_key()
key = self.rsa_pri.decrypt(key_data)
iv_data = self.get_iv()
iv = self.rsa_pri.decrypt(iv_data)
self.Set_AES_Key(key, iv)
def Test():
# 测试对象
#url = "http://101.245.108.250:8012"
url = "http://localhost:8012"
# 创建交流对象
comm = PmcComm(url)
comm.Set_RSA_Pri_Key("private_key.pem")
comm.Try_Get_AES_Key()
#msg = comm.start("mgmt")
#msg = comm.kill(6488)
#msg =comm.clear()
msg = comm.get_list()
print(msg)
+21
View File
@@ -0,0 +1,21 @@
/*
* Area是某个形状上的一个区域,它没有自己的帧缓冲区
*
*
*/
#pragma once
#include "Drawable.h"
namespace qing {
class Area: public Drawable {
public:
/* Phase Plotting Method 相图法 */
Area(Drawable *d, int w, int h, int x, int y, int rotate, int fontsize);
Color _get(int x, int y) override; /* 实现接口 */
void _p(int x, int y, Color &clr) override;
virtual void flush(Drawable *n, bool border = false) override;
virtual void move(int x, int y);
protected:
Drawable *d; /* 持有的绘制主体 */
};
}
+72
View File
@@ -0,0 +1,72 @@
#pragma once
#include <iostream>
#include <map>
namespace qing {
/*
* C++ 的命令行
*/
class Arguments {
public:
Arguments(int argc, char **argv) {
for (int i=1; i<argc; ++i) {
std::string item = argv[i];
if (item.length() > 1 && item[0] == '-' && item[1] != '-') {
for (unsigned long j=1; j<item.length(); ++j) {
if (item.length() == 2 && i < argc-1 && argv[i+1][0] != '-') {
std::string args = "";
while (i < argc-1 && argv[i+1][0] != '-') {
args += (args == "") ? "" : " ";
args += std::string(argv[++i]);
}
mc.insert(std::pair(item[j], args));
std::cout << item[j] << "=" << args << std::endl;
} else {
mc.insert(std::pair(item[j], ""));
std::cout << item[j] << std::endl;
}
}
} else if (item.length() > 2 && item[0] == '-' && item[1] == '-' && item[2] != '-') {
std::string arg = get_long_param_key(item);
size_t pos =arg.find('=');
if (pos == (long unsigned int)-1) {
std::cout << arg << std::endl;
m.insert(std::pair<std::string, std::string>(arg, ""));
} else {
std::string k = arg.substr(0, pos);
std::string v = arg.substr(pos+1, arg.length()-pos-1);
std::cout << k << "=" << v << std::endl;
m.insert(std::pair<std::string, std::string>(k, v));
}
} else {
throw WrongArgument(item);
}
}
}
std::map<std::string, std::string> &get_m() {
return m;
}
std::map<char, std::string> &get_mc() {
return mc;
}
class WrongArgument: std::runtime_error { /* 错的参数 */
public:
WrongArgument(std::string key): std::runtime_error(key) {}
};
private:
std::map<std::string, std::string> m;
std::map<char, std::string> mc;
std::string get_long_param_key(std::string &s) {
return s.substr(2, s.length() -2);
}
};
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include "lci/Shape.h"
#include "lci/字体绘制器.h"
namespace qing {
class CharShape: public Shape {
public:
CharShape(int w, int h, int x, int y, wchar_t wc)
: Shape(w, h, x, y), wc(wc), 绘制(w, h) {}
virtual void Draw(Drawable &d, Color clr) override{
int w = getw();
int h = geth();
int x = getx();
int y = gety();
绘制.drawvfont(&d, x, y, wc, clr);
}
private:
wchar_t wc;
字体绘制器 绘制;
};
}
+30
View File
@@ -0,0 +1,30 @@
#pragma once
#include "Area.h"
#include "InputBox.h"
#include <memory>
#include <mutex>
namespace qing {
/*
* CmdArea(命令区域)是一种特殊的Area区域
*
* 基层绘制方法我们已经在Area中实现了
*/
class CmdArea: public Area {
public:
CmdArea(Drawable *d, int w, int h, int x, int y, int rotate =0, int fontsize=18, bool hasBox = false);
void Input(char c);
void ClearInput();
virtual void print(char *utf8, int d=0); /* FIXME: d不知道是什么*/
void update_input();
void delete_input();
void clearBox();
std::string get_input_and_clear();
private:
int x_f=0, y_f=0;
std::unique_ptr<InputBox> area;
std::mutex lock;
std::string input = "";
void handle_ansi_code(char *buffer);
bool hasBox;
};
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
namespace qing {
/* 32位色彩和16位色彩 */
class Color{
public:
unsigned char r=0,g=0,b=0;
Color(unsigned char r=0, unsigned char g=0, unsigned char b=0) {
this->r=r;
this->g=g;
this->b=b;
}
};
extern const Color black;
extern const Color white;
extern const Color green;
extern const Color blue;
extern const Color red;
/* FIXME: 完成这个类定义 */
class Color16 {
public:
};
}
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include <iostream>
#include <stdexcept>
#include <thread>
#include "fsm.h"
#include "ThreadInterface.h"
/*
@qing:包含一个简易的多线程控制机制。
需要共享内存的变量和函数声明为全局变量(通常需要带锁),
线程中单独使用的变量和函数声明到属于它的命名空间中。
每个线程的主函数包括了对其的流程控制代码。
stop,setup,loop,clean函数中填入每个状态下的执行代码。
其中需要用到的函数,又实现在后边。
*/
namespace qing {
namespace lci{
//线程的主函数
//-----------------------------------------------------------
/*通常情况下,线程的主函数。*/
//前向声明脚本类型
class CommonThread: public lci::Fsm, ThreadInterface {
//一个持有资源线程的状态机类型
public:
//构造函数.
//没有必要输入打印机、脚本、线程的名字,放到全局变量
CommonThread(std::string name);
//暂时删除复制构造函数
CommonThread(CommonThread&) = delete;
//如果把析构函数放在这里,将会导致纯虚函数错误
virtual ~CommonThread();
//对线程名字的设置
void SetLabel(std::string name);
//对线程名字的获取
std::string& GetLabel();
//-----------------------------------------------------------
//等待线程启动完毕
//线程启动失败返回失败。线程启动成功或者现程结束返回成功。
//
//这个函数在某些情况下有可能会一直等待,直到循环结束
bool WaitStart(unsigned long n);
void WaitClose();
virtual void StopEvent() override;
virtual void WakeEvent() override;
virtual void LoopEvent() override;
virtual void ClearEvent() override;
void thread_main();
private:
//线程的名字
std::string label;
//用来存放该子线程的标识符。
std::thread *thread = NULL;
};
}
}
+89
View File
@@ -0,0 +1,89 @@
/*
* 这个是个抽象类,子类实现抽象的内部接口后
*
* 可以调用它已经写好的绘制方法。
*/
#pragma once
#include "Rectangle.h"
#include "Color.h"
#include <iostream>
/* 用于文字的绘制
* FIXME: 使用类封装 */
extern unsigned char ttu[];
extern int ttunumber;
extern unsigned char zzhi[];
extern int zzhinumber;
namespace qing {
class Drawable {
public:
/* 表示旋转方向 */
enum class Rotate: char {
R_0 = 0,
R_90 = 1,
R_180 = 2,
R_270 = 3
};
/* 非法的旋转模式 */
class InvalidRotateMode: std::runtime_error {
public:
InvalidRotateMode(): std::runtime_error("Invalid rotate mode.") {}
};
/*====== attr 属性区(访问器) ======*/
Drawable(int w, int h, int x, int y, int rotate, int fontsize);
virtual Rectangle get_size();
virtual Rectangle get_pos();
virtual void set_pos(Rectangle rect);
virtual Color get_color(int x, int y);
virtual void set_color(Color &color);
void FontSize(int fontsize);
int FontSize_get();
virtual Color& getFontsColor();
/*------ basic 基本 ------
* 这些类需要由子类分别进行实现,但是子类不直接调用,
* 而是由抽象基类中实现的方法调用 */
virtual Color _get(int x, int y) = 0; /* 获取像素点像素 */
virtual void _p(int x, int y, Color &clr) = 0; /* 绘制像素点像素 */
//virtual void _up(int x, int y, int w, int h, int level) = 0; /* FIXME: 不使用屏幕移动函数 */
//virtual void _down(int x, int y, int w, int h, int level) = 0;
//virtual void _left(int x, int y, int w, int h, int level) = 0;
//virtual void _right(int x, int y, int w, int h, int level) = 0;
//virtual void _clear(int x, int y, int w, int h) = 0; /* FIXME: 实现该方法 */
/*====== drawer 绘制 ======
* 提供给子类和外部的绘制方法 */
virtual void point(int x, int y, Color &clr);
virtual void line(int x1, int y1, int x2, int y2, Color &clr);
virtual void rectangle(int x, int y, int w, int h, Color &clr);
virtual void rectangle_fill(int x, int y, int w, int h, Color &clr);
virtual void flush(Drawable *n, bool border) = 0;
//virtual void clear(); /* 对于区域来说,清理可能较为复杂 */
/*------ printer 打印 ------
* 打印文字
*
* 只实现了打印文字的基本方法 */
void drawvfont(int x, int y, unsigned short z, Color &clr);
/*------ test 测试 ------
* 绘制测试图案 */
virtual void test(); /* 颜色测试 */
protected:
/*------ window ------
* 窗口属性 */
int w, h, x, y, rotate=0;
/*------ fonts lib ------
* 文字库 */
int font_w, font_h;
unsigned short *chss = (unsigned short *)zzhi;
unsigned char *vec = ttu;
int zn;
int vennum;
/* 绘制文字 */
unsigned char *drawvect(int x, int y, unsigned char *vect, int width, int high, Color &clr);
/*------ printer ------*/
Color clr = green;
//Color clr = white;
};
}
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include "Rectangle.h"
#include "Color.h"
#include "Drawable.h"
#include <iostream>
#include <linux/fb.h>
namespace qing{
/* FIXME: 这个类的绘制点方法可能过于复杂 */
class FrameBuf: public Drawable { /* 帧缓冲区类 */
public:
FrameBuf(const char *path, int rotate, int fontsize);
FrameBuf(const FrameBuf&) = delete; /* 持有fd */
~FrameBuf();
int get_bpp(); /* bits_per_pixel 像素比特 */
virtual Color _get(int x, int y) override;
virtual void _p(int x, int y, Color &color) override;
virtual void flush(Drawable *d, bool border = false) override;
void clear(); /* FIXME: 也许把这个方法加到抽象父类中? */
class UnknowBPP: std::runtime_error { /* 未知标准的像素比特 */
public:
UnknowBPP(): std::runtime_error("Unknow the number of Bits Per Pixel.") {}
};
static const int _HEAD_LEN = 8;
private:
int d = -1;
struct fb_var_screeninfo var_info; /* 可变参数 */
struct fb_fix_screeninfo fix_info; /* 固定参数 */
int x_over = 0; /* 横轴过量 */
int line_jump = 0; /* 行跳 */
char *buf = nullptr; /* 缓冲区指针 */
/* 根据像素比特的不同分情况讨论 */
Color get16(__u32 x, __u32 y);
Color get32(__u32 x, __u32 y);
void p32(__u32 x, __u32 y, Color &color);
void p16(__u32 x, __u32 y, Color &color);
};
}
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "Area.h"
namespace qing {
class InputBox: public Area { /* 输入框 */
public:
InputBox(Drawable *d, int w, int h, int x, int y, int rotate, int fontsize);
virtual void print(char *utf8, int line);
};
}
+24
View File
@@ -0,0 +1,24 @@
#pragma once
#include <iostream>
#include <string>
#include <map>
#include <linux/input.h>
//#define MAX 64 /* FIXME: 这个是不是没用了 */
namespace qing {
class KeyboardEvent{
public:
KeyboardEvent();
~KeyboardEvent();
struct input_event get(); /* 获取键盘事件 */
class NoEvent: std::runtime_error{ /* 键盘无事件异常 */
public:
NoEvent(std::string s): std::runtime_error(s){}
};
private:
std::map<std::string, int> pool;
static bool is_event_with_number(const char *filename);
static void set_noblocking(int d);
int maxd();
static void print(struct input_event ev);
};
}
+53
View File
@@ -0,0 +1,53 @@
/*
* 控件池区域
*
* 能够容纳别的控件,不需要重写flush()方法
*/
#include <memory>
#include <unordered_map>
#include "lci/Area.h"
#include "lci/Drawable.h"
namespace qing {
class PoolArea: public Area{
public:
PoolArea(Drawable *d, int w, int h, int x, int y, int rotate, int fontsize)
:Area(d, w, h, x, y, rotate, fontsize) {};
void add(const std::string& name, const std::shared_ptr<Area>& item) {
umap.insert({name, item});
}
/* 访问控件 */
std::shared_ptr<Area>& operator[](const std::string& key) {
return umap.at(key);
}
/* 访问控件 - const */
const std::shared_ptr<Area>& operator[](const std::string& key) const {
return umap.at(key);
}
/* 安全访问 - 不抛异常 */
//std::shard_ptr<Area> get(const std::string& key) const {}
/* 检查是否存在 */
bool contains(const std::string& key) const {
return umap.find(key) != umap.end();
}
/* 获取控件数量 */
size_t size() const { return umap.size(); }
/* 迭代器支持 - 范围for循环 */
std::unordered_map<std::string, std::shared_ptr<Area>>::iterator begin() { return umap.begin(); }
std::unordered_map<std::string, std::shared_ptr<Area>>::iterator end() { return umap.end(); }
/* 迭代器支持 - const版本 */
std::unordered_map<std::string, std::shared_ptr<Area>>::const_iterator begin() const { return umap.begin(); }
std::unordered_map<std::string, std::shared_ptr<Area>>::const_iterator end() const { return umap.end(); }
/* FIXME: cbegin/cend 支持 */
private:
std::unordered_map<std::string, std::shared_ptr<Area>> umap;
};
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
namespace qing{
class Rectangle{ /* 矩形类 */
public:
int w=0; /* width */
int h=0; /* height */
int b=0; /* ? */
Rectangle(int w=0, int h=0, int b=0) {
this->w=w;
this->h=h;
this->b=b;
}
};
}
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include "lci/Drawable.h"
#include "lci/Color.h"
namespace qing {
/* 【形状】
* 抽象基类 */
class Shape {
public:
Shape(int w, int h, int x, int y)
: w(w), h(h), x(x), y(y) {}
virtual void Draw(Drawable &d, Color clr) = 0;
int getw() {
return w;
}
void setw(int w) {
this->w = w;
}
int geth() {
return h;
}
void seth(int h) {
this->h = h;
}
int getx() {
return x;
}
void setx(int x) {
this->x = x;
}
int gety() {
return y;
}
void sety(int y) {
this->y = y;
}
private:
int w, h;
int x, y;
};
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
/*
* 形状区域
*/
#include "lci/Area.h"
#include "lci/Shape.h"
#include "vector"
#include "memory"
namespace qing {
class ShapeArea: public Area {
public:
ShapeArea(Drawable *d, int w, int h, int x, int y, int rotate, int fontsize)
: Area(d, w, h, x, y, rotate, fontsize){}
size_t size() const { return list.size(); }
void add(const std::shared_ptr<Shape>& item) {
list.push_back(item);
}
Shape& operator[](const size_t idx) {
return *(list[idx]);
}
const Shape& operator[](const size_t idx) const {
return *(list[idx]);
}
std::vector<std::shared_ptr<Shape>>::iterator begin() { return list.begin(); }
std::vector<std::shared_ptr<Shape>>::iterator end() { return list.end(); }
std::vector<std::shared_ptr<Shape>>::const_iterator begin() const { return list.begin(); }
std::vector<std::shared_ptr<Shape>>::const_iterator end() const { return list.end(); }
private:
std::vector<std::shared_ptr<Shape>> list;
};
}
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include <iostream>
#include <cstring>
#include <fcntl.h>
#include <unistd.h>
namespace qing {
class Term {
public:
/* 需要输入当前的控制台号(源头tty) */
Term(int target_tty, int origin_tty) {
this->origin_tty = origin_tty;
chvt(target_tty);
HideCursor(target_tty);
}
~Term(){ /* 切回源tty */
chvt(origin_tty);
}
private:
int origin_tty;
static int open_tty(int num) {
char path[16] = {'\0'};
snprintf(path, sizeof(path), "/dev/tty%d", num);
int d = open(path, O_WRONLY);
if (d == -1)
throw std::runtime_error(path);
else
return d;
}
static void HideCursor(int id) {
int d = open_tty(id);
const char *hide = "\033[?25l";
if (write(d, hide, strlen(hide)) == -1){
close(d);
throw std::runtime_error("term writting");
}
close(d);
}
static void chvt(int id) {
char cmd[16] = {'\0'};
snprintf(cmd, sizeof(cmd), "chvt %d", id);
int err = system(cmd);
if (err)
throw std::runtime_error("chvt");
}
};
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
namespace qing {
class ThreadInterface {
public:
//---------------------------------------------
//程序睡眠阶段的操作函数。
virtual void StopEvent() = 0;
//程序设置阶段的操作函数。
virtual void WakeEvent() = 0;
//程序运行阶段的操作函数。
virtual void LoopEvent() = 0;
//程序清理阶段的操作函数。
virtual void ClearEvent() = 0;
//可以将操作函数作为一种事件,可以从外界传入写好的事件函数,
//那么就需要将操作函数设定为静态函数
};
}
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include <iostream>
#include <cstddef>
namespace qing {
/* ANSI是WINDOWS操作系统默认的编码格式。 */
class ansi{
public:
static int size_gb18030(unsigned char *c, size_t maxLen);
static int size_gb2312(unsigned char first_byte);
class WrongLength: public std::runtime_error { /* 非法的长度 */
public:
WrongLength(): std::runtime_error("") {}
};
};
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include<mutex>
#include<condition_variable>
namespace qing{
namespace lci{
//用来存储状态的枚举类型
enum Stat{
SSHUT = -1,
SSTOP = 0,
SSTART = 1,
SRUNNING = 2
};//Status
class Fsm {//FiniteStateMachine有限状态机
public:
//构造函数
Fsm();
//检查状态
enum Stat chk();
//改变状态
void wake();
void stop();
void shut();
void run();
//protected:
//等待事件唤醒
void suspend();
private:
//锁类型,确保状态机线程安全
std::mutex lock;
//条件变量,用来执行等待或者唤醒
std::condition_variable cv;
//存放状态机的状态
enum Stat stat;
};//StateMachine
}//lci
}//qing
+160
View File
@@ -0,0 +1,160 @@
#pragma once
#include "Area.h"
#include "CmdArea.h"
#include "CommonThread.h"
namespace qing {
namespace lci{
/* PrintThread 打印线程 */
class p_th: public CommonThread {
public:
/**
* 构造函数 传入要读取的控制台设备文件描述符,背景画布,以及命令行区域
*/
p_th(std::string name, int masterfd, Drawable *back, CmdArea *font)
: CommonThread(name) {
this->masterfd = masterfd;
this->back = back;
this->font = font;
}
/**
* 析构函数 关闭线程
*
* 我在可控制线程中主要遇到的问题便是线程的自主终止,
*
* 在 Python 中似乎是只能实现可控制线程的手动退出的,
*
* 我在想是不是在cpp中也做成那样。
*
*
* FIXME(20260108): 啥意思不懂
*/
~p_th(){
/* 使线程自主关闭 */
if (this->chk() != lci::SSHUT){
this->shut();
}
/* 等待线程退出 */
this->WaitClose();
}
/**
* 暂时删除复制构造函数,因为复制出一个与原来线程无关的空白线程似乎没有什么作用
*
* 如果让两个变量引用同一个线程,会使得它们在结束的时候出现错误。
*/
p_th(p_th&) = delete;
/**
* 线程睡眠阶段的操作函数,线程等待。
*
* suspend() 方法似乎不太稳定,我还得再测试测试。
*/
void StopEvent() override {
this->suspend();
}
/**
* 线程苏醒之后执行的操作,线程启动。
*
* 我不知道为什么要等待1秒。
*/
void WakeEvent() {
/* 将文件描述符集合清零 */
FD_ZERO(&read_fds);
/* 清空缓存字符串 */
output = "";
/* 设置完毕,线程进入运行态 */
this->run();
}
/**
* 线程运行阶段的循环体,线程运行。
*
* 使用 SELECT 进行缓冲区的探测。
*/
void LoopEvent() {
/* 对可读集合进行清零 */
//FD_ZERO(&read_fds);
/* 设置可读集合 */
//FD_SET(masterfd, &read_fds);
/**
* 仅在集合中不存在控制台描述符时,才往其中添加该描述符
*
* 在这个集合中,应该只会存在这一个文件描述符吧
*/
if (!FD_ISSET(masterfd, &read_fds))
FD_SET(masterfd, &read_fds);
/* 设置超时时间 */
timeout.tv_sec = 0;
timeout.tv_usec = 10000;
int ret = select(masterfd + 1, &read_fds, NULL, NULL, &timeout);
if (ret == -1) {
//throw std::runtime_error("select");
//this->stop();
return; /* qing(20260108): 为什么不停止 */
} else if (ret == 0) { /* No data to read 无值可读 */
if (output == "") return; /* 在空闲时进行打印*/
font->print((char*)output.c_str(), 0); /* 按次为单位进行刷新应该可以提高效率 20260108(qing): ?*/
back->flush(font, false);
output = "";
} else {
if (FD_ISSET(masterfd, &read_fds)) { /* 有值可读 */
nread = read(masterfd, buffer, sizeof(buffer));
if (nread > 0) {
buffer[nread] = '\0';
output += buffer;
}
}
}
}
/**
* 线程执行过后的清理函数,清理资源。
*
* 什么也不做。我们在唤醒时并未申请资源。
*/
void ClearEvent() {
;
}
private:
int masterfd; /* 对象中存储的要读取的控制台设备文件描述符。 */
Drawable *back; /* 对象中存储的背景画布指针 */
CmdArea *font; /* 对象中存储的命令行区域指针 */
std::string output; /* 从终端设备中读取的进程输出 */
//--------------------------------------------
/* 下面的变量是为了提高速度,避免重复申请 */
fd_set read_fds; /* 文件描述符集合,用于存储 SELECT 返回的可读区域 */
struct timeval timeout; /* 时间结构体 用于传入 SELECT 函数的超时时间 */
ssize_t nread; /* 这一次从控制台中所读取大小 */
char buffer[1024]; /* 存储从控制台中读取字符的缓冲区,一次最多读这么多 */
};
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
namespace qing {
class utf8{ /* */
public:
static int size(const unsigned char c);
static char *to_ansi(char *s);
};
}
+114
View File
@@ -0,0 +1,114 @@
/* 图层区域是能够绘制图片的区域类型 */
#define cimg_display 0
#define cimg_use_jpeg
#define cimg_use_png
#include <CImg.h>
#include "cn/中文化.hpp"
#include "lci/Area.h"
使用 命名空间 cimg_library;
命名空间 qing {
类 图层区域: 公开的 Area { /* 图层区域 */
公开的:
图层区域(Drawable *d, int w, int h, int x, int y, int rotate, int fontsize, CImg<unsigned char>& img)
: Area(d, w, h, x, y, rotate, fontsize) {
/* 储存图片,需要调用图片的构造函数 */
this->img = img;
}
空类型 涂写图像() {
单精度小数 区域长宽比 = (float)w / h;
单精度小数 图片长宽比 = (float)img.width() / img.height();
/* 长宽比越大,代表图片越横向细长
* 长宽比越小,代表图片越竖向细长
* 长宽比为1时,图片为正方形
*
* 图片长宽比较大时,取区域长
* 图片长宽比较小时,取区域高 */
逻辑型 长为标准 = 图片长宽比 > 区域长宽比;
如果(长为标准) {
auto fixed = scaleByWidth(this->img, this->w);
CImg<unsigned char> gray = 转灰度图(fixed);
auto start = (int)(this->h - gray.height()) / 2;
for (int y = 0; y < gray.height(); ++y) {
for (int x = 0; x < gray.width(); ++x){
auto xx = x;
auto yy = y + start;
auto clr1 = 颜色转换(gray(x, y), this->clr);
this->point(xx, yy, clr1);
}
}
}
否则{ /* 以宽为标准 */
auto fixed = scaleByHeight(this->img, this->h);
auto gray = 转灰度图(fixed);
auto start = (int)(this->w - gray.width()) / 2;
for (int y = 0; y < gray.height(); ++y) {
for (int x = 0; x < gray.width(); ++x){
auto xx = x + start;
auto yy = y;
auto clr1 = 颜色转换(gray(x, y), this->clr);
this->point(xx, yy, clr1);
}
}
}
}
私有的:
CImg<unsigned char> img;
/* 以长为标准缩放 */
static CImg<unsigned char> scaleByWidth(const CImg<unsigned char>& src, int newWidth) {
/* 计算缩放比例 */
float ratio = (float)newWidth / src.width();
int newHeight = (int)(src.height() * ratio);
/* 使用_resize方法进行缩放 */
return src.get_resize(newWidth, newHeight, -100, -100, 5);
}
/* 以高为标准缩放 */
static CImg<unsigned char> scaleByHeight(const CImg<unsigned char>& src, int newHeight) {
/* 计算缩放比例 */
float ratio = (float)newHeight / src.height();
int newWidth = (int)(src.width() * ratio);
/* 计算_resize方法进行缩放 */
return src.get_resize(newWidth, newHeight, -100, -100, 5);
}
/* 将灰度颜色转换为我们目前画笔的颜色 */
静态的 Color 颜色转换(无符号 字符 灰度, Color clr) {
单精度小数 红 = (单精度小数)clr.r / 255;
单精度小数 绿 = (单精度小数)clr.g / 255;
单精度小数 蓝 = (单精度小数)clr.b / 255;
自动的 结果 = Color(红* 灰度*255, 绿*灰度*255, 蓝*灰度*255);
返回 结果;
}
/* 将CImg对象转为灰度图 */
静态的 CImg<unsigned char> 转灰度图(CImg<unsigned char>& img) {
if (img.spectrum() == 3) {
return img.get_normalize(0, 255).get_RGBtoXYZ().get_channel(1);
}
else if (img.spectrum() ==4) {
auto rgb_img = t_4_to_3(img);
return rgb_img.get_RGBtoXYZ().get_channel(1);
}
else {
抛出异常 std::runtime_error("没有对应通道数的算法");
}
}
/* 将4通道图片转为3通道 */
静态的 CImg<unsigned char> t_4_to_3(CImg<unsigned char>& img) {
// 创建RGB图像(3通道)
CImg<unsigned char> rgb_img(img.width(), img.height(), 1, 3);
cimg_forXY(rgb_img, x, y) {
rgb_img(x, y, 0, 0) = img(x, y, 0, 0); // R
rgb_img(x, y, 0, 1) = img(x, y, 0, 1); // G
rgb_img(x, y, 0, 2) = img(x, y, 0, 2); // B
// 忽略alpha通道 img(x, y, 0, 3)
}
return rgb_img;
}
};
}
+121
View File
@@ -0,0 +1,121 @@
/*
* 用于在可绘制对象上绘制中文文字
*
* 绘制时将对象作为参数传入
*/
#include "cn/中文化.hpp"
#include "lci/Drawable.h"
/* 用于文字的绘制,只要不动就线程安全 */
extern unsigned char ttu[];
extern int ttunumber;
extern unsigned char zzhi[];
extern int zzhinumber;
命名空间 qing {
类 字体绘制器 {
私有的:
/*------ fonts lib ------
* 文字库 */
int font_w, font_h;
unsigned short *chss = (unsigned short *)zzhi;
unsigned char *vec = ttu;
int zn = zzhinumber;
int vennum = ttunumber;
unsigned char *drawvect(Drawable* d, int x, int y, unsigned char *vect, int width, int high, Color &clr)
// x, y是左上角坐标,vect指向矢量图数据,width和high分别代表要绘制的图的宽和高(好像宽需要=高)
{
if (!vect) return nullptr;
int i = 0, j = 1; // 指向第一对数字
unsigned char ix, iy; // 从数组取出的第一对坐标放在这
double x1, y1;//, x2, y2; // 绘制直线的实数的坐标
if (high == 0) high = width; // 调节高宽
int ii1, jj1, ii2, jj2; // 最后算出的绘制直线的坐标
int isend = 0; //是否到达曲线尾
int isallend = 0; // 是否结束整个图像
do {
ix = vect[i];
iy = vect[j];
isend = ix & 1; // 奇数代表曲线尾
isallend = iy & 1; // 奇数代表整个图像结束
if (isallend) {
i += 2;
break; // 整个图像结束返回
}
if (isend) { // 如果这里曲线结束什么也不画进到下一条曲线
i += 2;
j += 2;
continue;
}
ix /= 2;
iy /= 2;
x1 = ix;
y1 = iy;
x1 = width * x1 / 128.0;
y1 = high * y1 / 128.0;
ii1 = (int)(x1 + x);
jj1 = (int)(y1 + y);
int k;
for (k = 0; k < 100000; k++) {
//x2 = x1;
//y2 = y1;
ii2 = ii1;
jj2 = jj1;
i += 2;
j += 2;
ix = vect[i];
iy = vect[j];
isend = ix & 1; //奇数代表曲线尾
isallend = iy & 1; //奇数代表整个图像结束
ix /= 2;
iy /= 2;
x1 = ix;
y1 = iy;
x1 = width * x1 / 128.0;
y1 = high * y1 / 128.0;
ii1 = (int)(x1 + x);
jj1 = (int)(y1 + y);
d->line(ii1, jj1, ii2, jj2, clr);
if (isend || isallend)
break;
}
i += 2; //指向下一条曲线
j += 2;
} while (!isallend);
return vect + i;
}
公开的:
字体绘制器(const int font_w, const int font_h)
: font_w(font_w), font_h(font_h) {}
/* 绘制文字 */
void drawvfont(Drawable* d, int x, int y, unsigned short z, Color &clr) {
int i, j, k, ii;
j = -1;
for (i = 0; i < zn; i++) {
if (chss[i] == z) {
j = i;
break;
}
}
if (j < 0) return;
k = 0;
for (i = 0; i < vennum; i++) {
if (k == j)
break;
for (ii =i; ii <vennum; ii++) {
if (vec[2*ii + 1] & 1) {
k++;
i = ii;
break;
}
}
}
if (k < j) return;
drawvect(d, x, y, vec + 2 * i, font_w, font_h, clr);
}
};
}
+141
View File
@@ -0,0 +1,141 @@
#pragma once
#include <iostream>
#include <vector>
#include <functional>
#include "../Dashun.h"
// 卷积神经网络构建辅助类(整合卷积、池化、展平层)
class CNNBuilder {
private:
std::vector<std::function<std::vector<float>(const std::vector<float>&, int, bool)>> forward_layers;
std::vector<std::function<std::vector<float>(const std::vector<float>&, int)>> backward_layers;
std::vector<std::tuple<std::string, int, int, int>> layer_shapes;
public:
// 添加卷积层
void add_conv_layer(ConvLayer& conv, int in_channels, int height, int width) {
conv.set_input_shape(height, width);
auto [out_height, out_width] = conv.get_output_shape();
forward_layers.push_back([&conv](const std::vector<float>& input, int batch, bool train) {
return conv.forward(input, batch, train);
});
backward_layers.push_back([&conv](const std::vector<float>& grad, int batch) {
auto a = conv.backward(grad, batch);
conv.update_parameters(0.3);
return a;
});
layer_shapes.push_back({"Conv", in_channels, out_height, out_width});
}
// 添加池化层
void add_pool_layer(PoolingLayer& pool, int in_channels, int height, int width) {
pool.set_input_shape(in_channels, height, width);
auto [out_channels, out_height, out_width] = pool.get_output_shape();
forward_layers.push_back([&pool](const std::vector<float>& input, int batch, bool train) {
return pool.forward(input, batch, train);
});
backward_layers.push_back([&pool](const std::vector<float>& grad, int batch) {
return pool.backward(grad, batch);
});
layer_shapes.push_back({"Pool", out_channels, out_height, out_width});
}
// 添加展平层
void add_flatten_layer(FlattenLayer& flatten, int in_channels, int height, int width) {
flatten.set_input_shape(in_channels, height, width);
int out_size = flatten.get_output_size();
forward_layers.push_back([&flatten](const std::vector<float>& input, int batch, bool train) {
return flatten.forward(input, batch, train);
});
backward_layers.push_back([&flatten](const std::vector<float>& grad, int batch) {
return flatten.backward(grad, batch);
});
layer_shapes.push_back({"Flatten", out_size, 1, 1});
}
// 前向传播(整个网络)
std::vector<float> forward(const std::vector<float>& input, int batch_size = 1, bool training = true) {
std::vector<float> current = input;
for (size_t i = 0; i < forward_layers.size(); i++) {
current = forward_layers[i](current, batch_size, training);
if (current.empty()) {
std::cerr << "Error in layer " << i << " ("
<< std::get<0>(layer_shapes[i]) << ")" << std::endl;
return std::vector<float>();
}
}
return current;
}
// 反向传播(整个网络)
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
std::vector<float> current_grad = grad_output;
for (int i = backward_layers.size() - 1; i >= 0; i--) {
current_grad = backward_layers[i](current_grad, batch_size);
if (current_grad.empty()) {
std::cerr << "Error in backward pass of layer " << i << " ("
<< std::get<0>(layer_shapes[i]) << ")" << std::endl;
return std::vector<float>();
}
}
return current_grad;
}
// 打印网络结构
void print_architecture() const {
std::cout << "CNN Architecture:" << std::endl;
std::cout << "=================" << std::endl;
for (size_t i = 0; i < layer_shapes.size(); i++) {
auto [layer_type, channels, height, width] = layer_shapes[i];
if (layer_type == "Flatten") {
std::cout << "Layer " << i << ": " << layer_type
<< " -> Output size: " << channels << std::endl;
} else {
std::cout << "Layer " << i << ": " << layer_type
<< " -> Output shape: " << channels
<< "x" << height << "x" << width << std::endl;
}
}
}
};
/*
// 构建简单CNN
ConvLayer conv1(3, 16, 3, 1, 1);
PoolingLayer pool1(2, 2, 2);
ConvLayer conv2(16, 32, 3, 1, 1);
PoolingLayer pool2(2, 2, 2);
FlattenLayer flatten;
// 使用CNN构建器
CNNBuilder cnn;
cnn.add_conv_layer(conv1, 3, 32, 32);
cnn.add_pool_layer(pool1, 16, 32, 32);
cnn.add_conv_layer(conv2, 16, 16, 16);
cnn.add_pool_layer(pool2, 32, 16, 16);
cnn.add_flatten_layer(flatten, 32, 8, 8);
// 执行前向传播
auto output = cnn.forward(input_data, batch_size, true);
*/
+562
View File
@@ -0,0 +1,562 @@
#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <chrono>
#include <random>
#include <numeric>
#include <functional>
class ConvLayer {
private:
// 参数
int input_channels;
int output_channels;
int kernel_size;
int stride;
int padding;
int input_height;
int input_width;
int output_height;
int output_width;
// Leaky ReLU参数
float negative_slope; // Leaky ReLU的负斜率,通常为0.01
// 权重和偏置
std::vector<float> weights; // shape: [output_channels, input_channels, kernel_size, kernel_size]
std::vector<float> bias; // shape: [output_channels]
// 梯度和动量
std::vector<float> d_weights; // 权重梯度
std::vector<float> d_bias; // 偏置梯度
std::vector<float> weight_momentum; // 动量
std::vector<float> bias_momentum; // 偏置动量
// 中间变量缓存(用于反向传播)
std::vector<float> col_buffer; // im2col后的矩阵
std::vector<float> input_cache; // 缓存的输入(用于计算输入梯度)
std::vector<float> output_cache; // 缓存的输出(激活前)
std::vector<float> activated_cache; // 激活后的输出缓存
std::vector<float> col_cache; // 缓存的col矩阵(用于权重梯度计算)
// 辅助函数
void im2col(const float* input, int batch_size);
void col2im(const float* col, float* im, int batch_size);
void sgemm(int M, int N, int K,
const float* A, const float* B, float* C,
bool transA = false, bool transB = false);
void sgemm_add(int M, int N, int K,
const float* A, const float* B, float* C,
float alpha = 1.0f, float beta = 1.0f,
bool transA = false, bool transB = false);
// Leaky ReLU激活函数及其导数
inline float leaky_relu(float x) const {
return x > 0 ? x : negative_slope * x;
}
inline float leaky_relu_derivative(float x) const {
return x > 0 ? 1.0f : negative_slope;
}
// 应用激活函数到整个张量
void apply_leaky_relu(float* data, int size) {
for (int i = 0; i < size; i++) {
data[i] = leaky_relu(data[i]);
}
}
// 计算激活函数的梯度
void compute_activation_gradient(const float* pre_activation, float* gradient, int size) {
for (int i = 0; i < size; i++) {
gradient[i] *= leaky_relu_derivative(pre_activation[i]);
}
}
public:
// 构造函数 - 添加negative_slope参数
ConvLayer(int in_channels, int out_channels,
int kernel_size, int stride = 1, int padding = 0,
float negative_slope = 0.01f)
: input_channels(in_channels),
output_channels(out_channels),
kernel_size(kernel_size),
stride(stride),
padding(padding),
negative_slope(negative_slope) {
// 初始化权重和偏置
int weight_size = out_channels * in_channels * kernel_size * kernel_size;
weights.resize(weight_size);
d_weights.resize(weight_size, 0.0f);
weight_momentum.resize(weight_size, 0.0f);
bias.resize(out_channels);
d_bias.resize(out_channels, 0.0f);
bias_momentum.resize(out_channels, 0.0f);
// He初始化(适合ReLU激活函数)
float std_dev = sqrt(2.0f / (in_channels * kernel_size * kernel_size));
std::default_random_engine generator;
std::normal_distribution<float> distribution(0.0f, std_dev);
for (int i = 0; i < weight_size; i++) {
weights[i] = distribution(generator);
}
for (int i = 0; i < out_channels; i++) {
bias[i] = 0.1f; // 小的正偏置
}
}
// 设置输入尺寸(计算输出尺寸)
void set_input_shape(int height, int width) {
input_height = height;
input_width = width;
output_height = (height + 2 * padding - kernel_size) / stride + 1;
output_width = (width + 2 * padding - kernel_size) / stride + 1;
// 预分配缓冲区
int col_height = input_channels * kernel_size * kernel_size;
int col_width = output_height * output_width;
col_buffer.resize(col_height * col_width);
// 清空缓存
input_cache.clear();
output_cache.clear();
activated_cache.clear();
col_cache.clear();
}
// 前向传播
std::vector<float> forward(const std::vector<float>& input, int batch_size = 1, bool training = true) {
// 计算输出大小
int output_size = batch_size * output_channels * output_height * output_width;
std::vector<float> output(output_size, 0.0f);
std::vector<float> pre_activation(output_size, 0.0f); // 激活前的输出
// 如果训练模式,缓存输入
if (training) {
input_cache = input;
}
// 对batch中的每个样本进行处理
for (int b = 0; b < batch_size; b++) {
const float* input_ptr = input.data() + b * input_channels * input_height * input_width;
// 1. im2col转换
im2col(input_ptr, 1);
// 2. 矩阵乘法:weights * col_buffer
int M = output_channels; // 输出通道数
int N = output_height * output_width; // 输出特征图大小
int K = input_channels * kernel_size * kernel_size; // 卷积核展平大小
// 如果训练模式,缓存col矩阵
if (training && b == 0) {
col_cache = col_buffer;
}
// 临时存储矩阵乘法结果(激活前)
std::vector<float> gemm_result(M * N, 0.0f);
sgemm(M, N, K, weights.data(), col_buffer.data(), gemm_result.data());
// 3. 加上偏置并存储到pre_activation
float* pre_activation_ptr = pre_activation.data() + b * output_channels * output_height * output_width;
float* output_ptr = output.data() + b * output_channels * output_height * output_width;
for (int oc = 0; oc < output_channels; oc++) {
float bias_val = bias[oc];
for (int i = 0; i < output_height * output_width; i++) {
float value = gemm_result[oc * N + i] + bias_val;
pre_activation_ptr[oc * output_height * output_width + i] = value;
// 4. 应用Leaky ReLU激活函数
output_ptr[oc * output_height * output_width + i] = leaky_relu(value);
}
}
}
// 如果训练模式,缓存激活前和激活后的输出
if (training) {
output_cache = pre_activation; // 缓存激活前的输出
activated_cache = output; // 缓存激活后的输出
}
return output; // 返回激活后的输出
}
// 反向传播
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
// grad_output是激活函数后的梯度,需要先计算激活函数前的梯度
std::vector<float> grad_pre_activation(grad_output);
// 计算激活函数的梯度:grad_pre_activation = grad_output * leaky_relu_derivative(pre_activation)
for (int b = 0; b < batch_size; b++) {
const float* pre_activation_ptr = output_cache.data() + b * output_channels * output_height * output_width;
float* grad_ptr = grad_pre_activation.data() + b * output_channels * output_height * output_width;
for (int i = 0; i < output_channels * output_height * output_width; i++) {
grad_ptr[i] *= leaky_relu_derivative(pre_activation_ptr[i]);
}
}
// 1. 计算偏置梯度: d_bias = sum(grad_pre_activation, axis=(0,2,3))
std::fill(d_bias.begin(), d_bias.end(), 0.0f);
for (int b = 0; b < batch_size; b++) {
const float* grad_ptr = grad_pre_activation.data() + b * output_channels * output_height * output_width;
for (int oc = 0; oc < output_channels; oc++) {
float sum = 0.0f;
for (int i = 0; i < output_height * output_width; i++) {
sum += grad_ptr[oc * output_height * output_width + i];
}
d_bias[oc] += sum;
}
}
// 2. 计算权重梯度: d_weights = grad_pre_activation_col * col_cache^T
std::fill(d_weights.begin(), d_weights.end(), 0.0f);
int M = output_channels; // 输出通道数
int N = input_channels * kernel_size * kernel_size; // 卷积核展平大小
int K = output_height * output_width; // 输出特征图大小
// 将grad_pre_activation reshape为矩阵 [output_channels, batch_size * output_height * output_width]
std::vector<float> grad_pre_activation_mat(M * K * batch_size, 0.0f);
for (int b = 0; b < batch_size; b++) {
const float* grad_ptr = grad_pre_activation.data() + b * output_channels * output_height * output_width;
for (int oc = 0; oc < output_channels; oc++) {
for (int i = 0; i < output_height * output_width; i++) {
grad_pre_activation_mat[oc * (K * batch_size) + b * K + i] =
grad_ptr[oc * output_height * output_width + i];
}
}
}
// 扩展col_cache以匹配batch大小
std::vector<float> col_cache_batched(N * K * batch_size, 0.0f);
for (int b = 0; b < batch_size; b++) {
std::copy(col_cache.begin(), col_cache.end(),
col_cache_batched.begin() + b * N * K);
}
// 矩阵乘法: d_weights = grad_pre_activation_mat * col_cache_batched^T
// grad_pre_activation_mat: [M, K*batch_size], col_cache_batched^T: [K*batch_size, N]
sgemm_add(M, N, K * batch_size,
grad_pre_activation_mat.data(), col_cache_batched.data(), d_weights.data(),
1.0f, 0.0f, false, true);
// 3. 计算输入梯度: d_input_col = weights^T * grad_pre_activation_mat
// 然后通过col2im将d_input_col转换为d_input
std::vector<float> d_input(batch_size * input_channels * input_height * input_width, 0.0f);
// 计算d_input_col
std::vector<float> d_input_col(N * K * batch_size, 0.0f);
sgemm(N, K * batch_size, M,
weights.data(), grad_pre_activation_mat.data(), d_input_col.data(),
true, false);
// 将d_input_col通过col2im转换回输入形状
for (int b = 0; b < batch_size; b++) {
float* d_input_ptr = d_input.data() + b * input_channels * input_height * input_width;
col2im(d_input_col.data() + b * N * K, d_input_ptr, 1);
}
return d_input;
}
// 更新参数(使用SGD with momentum)
void update_parameters(float learning_rate = 0.01f, float momentum = 0.9f) {
// 更新权重
for (size_t i = 0; i < weights.size(); i++) {
weight_momentum[i] = momentum * weight_momentum[i] - learning_rate * d_weights[i];
weights[i] += weight_momentum[i];
}
// 更新偏置
for (size_t i = 0; i < bias.size(); i++) {
bias_momentum[i] = momentum * bias_momentum[i] - learning_rate * d_bias[i];
bias[i] += bias_momentum[i];
}
// 重置梯度(可选)
// std::fill(d_weights.begin(), d_weights.end(), 0.0f);
// std::fill(d_bias.begin(), d_bias.end(), 0.0f);
}
// Adam优化器更新参数
void update_parameters_adam(float learning_rate = 0.001f,
float beta1 = 0.9f, float beta2 = 0.999f,
float epsilon = 1e-8f, int t = 1) {
static std::vector<float> m_weights(weights.size(), 0.0f);
static std::vector<float> v_weights(weights.size(), 0.0f);
static std::vector<float> m_bias(bias.size(), 0.0f);
static std::vector<float> v_bias(bias.size(), 0.0f);
// 更新权重的一阶矩和二阶矩估计
for (size_t i = 0; i < weights.size(); i++) {
m_weights[i] = beta1 * m_weights[i] + (1 - beta1) * d_weights[i];
v_weights[i] = beta2 * v_weights[i] + (1 - beta2) * d_weights[i] * d_weights[i];
// 偏差校正
float m_hat = m_weights[i] / (1 - powf(beta1, t));
float v_hat = v_weights[i] / (1 - powf(beta2, t));
// 更新权重
weights[i] -= learning_rate * m_hat / (sqrtf(v_hat) + epsilon);
}
// 更新偏置的一阶矩和二阶矩估计
for (size_t i = 0; i < bias.size(); i++) {
m_bias[i] = beta1 * m_bias[i] + (1 - beta1) * d_bias[i];
v_bias[i] = beta2 * v_bias[i] + (1 - beta2) * d_bias[i] * d_bias[i];
// 偏差校正
float m_hat = m_bias[i] / (1 - powf(beta1, t));
float v_hat = v_bias[i] / (1 - powf(beta2, t));
// 更新偏置
bias[i] -= learning_rate * m_hat / (sqrtf(v_hat) + epsilon);
}
}
// 获取参数
const std::vector<float>& get_weights() const { return weights; }
const std::vector<float>& get_bias() const { return bias; }
const std::vector<float>& get_weight_gradients() const { return d_weights; }
const std::vector<float>& get_bias_gradients() const { return d_bias; }
// 设置参数(用于加载预训练模型)
void set_weights(const std::vector<float>& new_weights) { weights = new_weights; }
void set_bias(const std::vector<float>& new_bias) { bias = new_bias; }
// 设置Leaky ReLU参数
void set_leaky_relu_slope(float slope) { negative_slope = slope; }
// 获取输出尺寸
std::tuple<int, int> get_output_shape() const {
return std::make_tuple(output_height, output_width);
}
// 梯度检查(用于调试)
float gradient_check(const std::vector<float>& input,
const std::vector<float>& grad_output,
int weight_idx, float epsilon = 1e-5f) {
// 保存原始权重
float original_weight = weights[weight_idx];
// 计算f(x + epsilon)
weights[weight_idx] = original_weight + epsilon;
auto output_plus = forward(input, 1, false);
// 计算f(x - epsilon)
weights[weight_idx] = original_weight - epsilon;
auto output_minus = forward(input, 1, false);
// 恢复原始权重
weights[weight_idx] = original_weight;
// 计算数值梯度
float numerical_grad = 0.0f;
for (size_t i = 0; i < output_plus.size(); i++) {
numerical_grad += grad_output[i] * (output_plus[i] - output_minus[i]) / (2 * epsilon);
}
// 返回梯度差异
return fabs(numerical_grad - d_weights[weight_idx]);
}
};
// im2col实现 - 将图像转换为矩阵
void ConvLayer::im2col(const float* input, int batch_size) {
int col_height = input_channels * kernel_size * kernel_size;
int col_width = output_height * output_width;
int input_h_padded = input_height + 2 * padding;
int input_w_padded = input_width + 2 * padding;
// 创建填充后的输入(如果需要)
std::vector<float> padded_input;
const float* input_data = input;
if (padding > 0) {
padded_input.resize(input_channels * input_h_padded * input_w_padded, 0.0f);
for (int c = 0; c < input_channels; c++) {
for (int h = 0; h < input_height; h++) {
for (int w = 0; w < input_width; w++) {
int padded_idx = c * input_h_padded * input_w_padded +
(h + padding) * input_h_padded + (w + padding);
int orig_idx = c * input_height * input_width + h * input_width + w;
padded_input[padded_idx] = input[orig_idx];
}
}
}
input_data = padded_input.data();
}
// 执行im2col
int col_idx = 0;
for (int c = 0; c < input_channels; c++) {
for (int kh = 0; kh < kernel_size; kh++) {
for (int kw = 0; kw < kernel_size; kw++) {
for (int h = 0; h < output_height; h++) {
for (int w = 0; w < output_width; w++) {
int input_h = h * stride + kh;
int input_w = w * stride + kw;
int input_idx = c * input_h_padded * input_w_padded +
input_h * input_w_padded + input_w;
col_buffer[col_idx++] = input_data[input_idx];
}
}
}
}
}
}
// col2im实现 - 将矩阵转换回图像(用于梯度传播)
void ConvLayer::col2im(const float* col, float* im, int batch_size) {
int col_height = input_channels * kernel_size * kernel_size;
int col_width = output_height * output_width;
int input_h_padded = input_height + 2 * padding;
int input_w_padded = input_width + 2 * padding;
// 创建填充后的图像缓冲区
std::vector<float> padded_im(input_channels * input_h_padded * input_w_padded, 0.0f);
// 执行col2im
int col_idx = 0;
for (int c = 0; c < input_channels; c++) {
for (int kh = 0; kh < kernel_size; kh++) {
for (int kw = 0; kw < kernel_size; kw++) {
for (int h = 0; h < output_height; h++) {
for (int w = 0; w < output_width; w++) {
int input_h = h * stride + kh;
int input_w = w * stride + kw;
int input_idx = c * input_h_padded * input_w_padded +
input_h * input_w_padded + input_w;
padded_im[input_idx] += col[col_idx++];
}
}
}
}
}
// 去除padding,得到原始输入梯度
if (padding > 0) {
for (int c = 0; c < input_channels; c++) {
for (int h = 0; h < input_height; h++) {
for (int w = 0; w < input_width; w++) {
int padded_idx = c * input_h_padded * input_w_padded +
(h + padding) * input_w_padded + (w + padding);
int orig_idx = c * input_height * input_width + h * input_width + w;
im[orig_idx] = padded_im[padded_idx];
}
}
}
} else {
std::copy(padded_im.begin(), padded_im.end(), im);
}
}
// 优化的矩阵乘法
void ConvLayer::sgemm(int M, int N, int K,
const float* A, const float* B, float* C,
bool transA, bool transB) {
// 初始化输出为0
std::memset(C, 0, M * N * sizeof(float));
// 调用带参数的版本
sgemm_add(M, N, K, A, B, C, 1.0f, 0.0f, transA, transB);
}
// 带参数的矩阵乘法(支持转置)
void ConvLayer::sgemm_add(int M, int N, int K,
const float* A, const float* B, float* C,
float alpha, float beta,
bool transA, bool transB) {
// 如果beta为0,清空C
if (beta == 0.0f) {
std::memset(C, 0, M * N * sizeof(float));
}
// 优化:使用循环展开和缓存友好的访问模式
const int BLOCK_SIZE = 4;
// 处理A转置的情况
if (transA && !transB) {
// C = A^T * B
for (int i = 0; i < M; i++) {
for (int k = 0; k < K; k++) {
float a = A[k * M + i] * alpha;
if (a == 0.0f) continue;
const float* B_ptr = B + k * N;
float* C_ptr = C + i * N;
int j = 0;
for (; j <= N - BLOCK_SIZE; j += BLOCK_SIZE) {
C_ptr[j] += a * B_ptr[j];
C_ptr[j + 1] += a * B_ptr[j + 1];
C_ptr[j + 2] += a * B_ptr[j + 2];
C_ptr[j + 3] += a * B_ptr[j + 3];
}
for (; j < N; j++) {
C_ptr[j] += a * B_ptr[j];
}
}
}
} else if (!transA && transB) {
// C = A * B^T
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
float sum = 0.0f;
for (int k = 0; k < K; k++) {
sum += A[i * K + k] * B[j * K + k];
}
C[i * N + j] = beta * C[i * N + j] + alpha * sum;
}
}
} else if (transA && transB) {
// C = A^T * B^T = (B * A)^T
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
float sum = 0.0f;
for (int k = 0; k < K; k++) {
sum += A[k * M + i] * B[j * K + k];
}
C[i * N + j] = beta * C[i * N + j] + alpha * sum;
}
}
} else {
// C = A * B
for (int i = 0; i < M; i++) {
for (int k = 0; k < K; k++) {
float a = A[i * K + k] * alpha;
if (a == 0.0f) continue;
const float* B_ptr = B + k * N;
float* C_ptr = C + i * N;
int j = 0;
for (; j <= N - BLOCK_SIZE; j += BLOCK_SIZE) {
C_ptr[j] += a * B_ptr[j];
C_ptr[j + 1] += a * B_ptr[j + 1];
C_ptr[j + 2] += a * B_ptr[j + 2];
C_ptr[j + 3] += a * B_ptr[j + 3];
}
for (; j < N; j++) {
C_ptr[j] += a * B_ptr[j];
}
}
}
}
}
+366
View File
@@ -0,0 +1,366 @@
#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <tuple>
#include "ConvLayer.hpp"
class FlattenLayer {
private:
// 输入输出尺寸
int batch_size;
int input_channels;
int input_height;
int input_width;
int output_size;
// 缓存输入形状(用于反向传播)
std::vector<int> input_shape_cache;
std::vector<float> input_cache; // 缓存输入数据(可选)
// 辅助函数
//void flatten_forward(const float* input, float* output);
//void flatten_backward(const float* grad_output, float* grad_input);
public:
// 构造函数
FlattenLayer() : batch_size(0), input_channels(0),
input_height(0), input_width(0), output_size(0) {}
// 设置输入尺寸(自动计算输出尺寸)
void set_input_shape(int channels, int height, int width, int batch = 1) {
input_channels = channels;
input_height = height;
input_width = width;
batch_size = batch;
// 计算输出大小
output_size = channels * height * width;
// 缓存输入形状
input_shape_cache = {channels, height, width, batch};
}
// 设置输入尺寸(从卷积/池化层获取)
template<typename... Args>
void set_input_shape(std::tuple<Args...> shape_tuple) {
set_input_shape(std::get<0>(shape_tuple),
std::get<1>(shape_tuple),
std::get<2>(shape_tuple));
}
// 前向传播
std::vector<float> forward(const std::vector<float>& input, int batch_size = 1, bool training = true) {
// 如果之前没有设置输入尺寸,自动推断
if (input_channels == 0 && input_height == 0 && input_width == 0) {
// 这里假设输入是完整的batch数据,需要外部调用set_input_shape
// 或者根据输入大小推断(但不知道高度和宽度)
std::cerr << "Warning: Input shape not set for FlattenLayer. "
<< "Please call set_input_shape() first." << std::endl;
return input; // 安全返回,但可能不正确
}
// 检查输入大小是否匹配
int expected_input_size = batch_size * input_channels * input_height * input_width;
if (input.size() != static_cast<size_t>(expected_input_size)) {
std::cerr << "Error: Input size mismatch in FlattenLayer. "
<< "Expected: " << expected_input_size
<< ", Got: " << input.size() << std::endl;
return std::vector<float>();
}
// 如果训练模式,缓存输入形状
if (training) {
this->batch_size = batch_size;
input_shape_cache = {input_channels, input_height, input_width, batch_size};
input_cache = input; // 可选:缓存输入数据
}
// 计算输出大小
int output_total_size = batch_size * output_size;
std::vector<float> output(output_total_size, 0.0f);
// 执行展平操作
flatten_forward(input.data(), output.data());
return output;
}
// 反向传播
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
// 检查梯度大小是否匹配
int expected_grad_size = batch_size * output_size;
if (grad_output.size() != static_cast<size_t>(expected_grad_size)) {
std::cerr << "Error: Gradient size mismatch in FlattenLayer. "
<< "Expected: " << expected_grad_size
<< ", Got: " << grad_output.size() << std::endl;
return std::vector<float>();
}
// 恢复输入形状
if (input_shape_cache.size() >= 4) {
input_channels = input_shape_cache[0];
input_height = input_shape_cache[1];
input_width = input_shape_cache[2];
this->batch_size = input_shape_cache[3];
}
// 计算输入梯度大小
int input_total_size = this->batch_size * input_channels * input_height * input_width;
std::vector<float> grad_input(input_total_size, 0.0f);
// 执行反向展平操作
flatten_backward(grad_output.data(), grad_input.data());
return grad_input;
}
// 获取输出尺寸
int get_output_size() const {
return output_size;
}
// 获取输出尺寸(返回元组)
std::tuple<int> get_output_shape() const {
return std::make_tuple(output_size);
}
// 获取输入尺寸
std::tuple<int, int, int> get_input_shape() const {
return std::make_tuple(input_channels, input_height, input_width);
}
// 获取完整输入尺寸(包含批次)
std::tuple<int, int, int, int> get_full_input_shape() const {
return std::make_tuple(input_channels, input_height, input_width, batch_size);
}
// 打印层信息
void print_info() const {
std::cout << "Flatten Layer Info:" << std::endl;
std::cout << " Input Shape: " << input_channels << "x"
<< input_height << "x" << input_width;
if (batch_size > 0) {
std::cout << " (batch: " << batch_size << ")";
}
std::cout << std::endl;
std::cout << " Output Size: " << output_size;
if (batch_size > 0) {
std::cout << " (total: " << batch_size * output_size << ")";
}
std::cout << std::endl;
}
// 重置层状态
void reset() {
input_shape_cache.clear();
input_cache.clear();
batch_size = 0;
input_channels = 0;
input_height = 0;
input_width = 0;
output_size = 0;
}
// 检查是否已初始化
bool is_initialized() const {
return input_channels > 0 && input_height > 0 && input_width > 0;
}
private:
// 展平前向传播实现
void flatten_forward(const float* input, float* output) {
// 简单地将多维数据复制到一维数组
int elements_per_sample = input_channels * input_height * input_width;
for (int b = 0; b < batch_size; b++) {
const float* input_ptr = input + b * elements_per_sample;
float* output_ptr = output + b * output_size;
// 直接内存复制
std::copy(input_ptr, input_ptr + elements_per_sample, output_ptr);
// 或者使用循环(等效)
/*
for (int i = 0; i < elements_per_sample; i++) {
output_ptr[i] = input_ptr[i];
}
*/
}
}
// 展平反向传播实现
void flatten_backward(const float* grad_output, float* grad_input) {
// 反向传播只是将梯度重新整形为输入形状
int elements_per_sample = input_channels * input_height * input_width;
for (int b = 0; b < batch_size; b++) {
const float* grad_output_ptr = grad_output + b * output_size;
float* grad_input_ptr = grad_input + b * elements_per_sample;
// 直接内存复制
std::copy(grad_output_ptr, grad_output_ptr + elements_per_sample, grad_input_ptr);
// 或者使用循环(等效)
/*
for (int i = 0; i < elements_per_sample; i++) {
grad_input_ptr[i] = grad_output_ptr[i];
}
*/
}
}
};
// 动态展平层(自动推断输入形状)
class DynamicFlattenLayer {
private:
int batch_size;
int input_channels;
int input_height;
int input_width;
int output_size;
public:
DynamicFlattenLayer() : batch_size(0), input_channels(0),
input_height(0), input_width(0), output_size(0) {}
// 前向传播(自动推断形状)
std::vector<float> forward(const std::vector<float>& input,
std::tuple<int, int, int> input_shape,
int batch_size = 1,
bool training = true) {
// 从元组获取输入形状
input_channels = std::get<0>(input_shape);
input_height = std::get<1>(input_shape);
input_width = std::get<2>(input_shape);
this->batch_size = batch_size;
// 验证输入大小
int expected_size = batch_size * input_channels * input_height * input_width;
if (input.size() != static_cast<size_t>(expected_size)) {
std::cerr << "Error: Input size mismatch in DynamicFlattenLayer. "
<< "Expected: " << expected_size
<< ", Got: " << input.size()
<< " (shape: " << input_channels << "x"
<< input_height << "x" << input_width
<< ", batch: " << batch_size << ")" << std::endl;
return std::vector<float>();
}
// 计算输出大小
output_size = input_channels * input_height * input_width;
int total_output_size = batch_size * output_size;
std::vector<float> output(total_output_size);
// 执行展平
const float* input_ptr = input.data();
float* output_ptr = output.data();
int elements_per_sample = output_size;
for (int b = 0; b < batch_size; b++) {
std::copy(input_ptr, input_ptr + elements_per_sample, output_ptr);
input_ptr += elements_per_sample;
output_ptr += elements_per_sample;
}
return output;
}
// 反向传播
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
// 检查是否已初始化
if (output_size == 0) {
std::cerr << "Error: DynamicFlattenLayer not initialized. "
<< "Call forward() first." << std::endl;
return std::vector<float>();
}
// 验证梯度大小
int expected_grad_size = batch_size * output_size;
if (grad_output.size() != static_cast<size_t>(expected_grad_size)) {
std::cerr << "Error: Gradient size mismatch in DynamicFlattenLayer. "
<< "Expected: " << expected_grad_size
<< ", Got: " << grad_output.size() << std::endl;
return std::vector<float>();
}
// 恢复输入形状
int total_input_size = batch_size * input_channels * input_height * input_width;
std::vector<float> grad_input(total_input_size);
// 执行反向展平
const float* grad_output_ptr = grad_output.data();
float* grad_input_ptr = grad_input.data();
int elements_per_sample = output_size;
for (int b = 0; b < batch_size; b++) {
std::copy(grad_output_ptr, grad_output_ptr + elements_per_sample, grad_input_ptr);
grad_output_ptr += elements_per_sample;
grad_input_ptr += elements_per_sample;
}
return grad_input;
}
// 获取输出尺寸
int get_output_size() const { return output_size; }
// 获取输入形状
std::tuple<int, int, int> get_input_shape() const {
return std::make_tuple(input_channels, input_height, input_width);
}
};
// 使用示例函数
inline void flatten_layer_example() {
std::cout << "=== Flatten Layer Example ===" << std::endl;
// 创建展平层
FlattenLayer flatten;
// 设置输入形状(例如从池化层获取的输出)
// 假设池化层输出:64通道,7x7特征图
flatten.set_input_shape(64, 7, 7);
// 创建模拟输入数据(batch_size=2)
int batch_size = 2;
std::vector<float> input(2 * 64 * 7 * 7);
for (size_t i = 0; i < input.size(); i++) {
input[i] = static_cast<float>(i) / 100.0f;
}
// 前向传播
std::cout << "Forward pass..." << std::endl;
auto output = flatten.forward(input, batch_size, true);
std::cout << "Input size: " << input.size() << std::endl;
std::cout << "Output size: " << output.size() << std::endl;
std::cout << "Expected output size per sample: " << flatten.get_output_size() << std::endl;
// 打印部分结果
std::cout << "First 10 output values: ";
for (int i = 0; i < std::min(10, static_cast<int>(output.size())); i++) {
std::cout << output[i] << " ";
}
std::cout << std::endl;
// 创建模拟梯度(全为1)
std::vector<float> grad_output(output.size(), 1.0f);
// 反向传播
std::cout << "Backward pass..." << std::endl;
auto grad_input = flatten.backward(grad_output, batch_size);
std::cout << "Gradient input size: " << grad_input.size() << std::endl;
std::cout << "Expected gradient input size: " << 2 * 64 * 7 * 7 << std::endl;
// 验证形状正确性
auto [channels, height, width] = flatten.get_input_shape();
std::cout << "Recovered input shape: " << channels
<< "x" << height << "x" << width << std::endl;
// 打印层信息
flatten.print_info();
}
+66
View File
@@ -0,0 +1,66 @@
#pragma once
#include <cmath>
#include <iostream>
/* FIXME: 设置为每层可调节的 */
/* FIXME: 现在这个“接口”并未提供抽象函数 */
constexpr float LEAKY_RELU_ALPHA = 0.01;
using f_t = float(*)(float); /* FIXME: 在qing::Thread附近已经定义了f_t标识符 */
/* 神经网络层接口 */
class ILayer {
public:
/* 激活函数枚举类 */
enum class ActivationFunc {
None, ReLU, Leaky_ReLU, Sigmoid, Tanh
};
/* 获取类型名 */
static const char* get_f_type(ActivationFunc& af);
/* 通过类型名获取实体 */
static ActivationFunc parse_ff_type(const std::string& name);
/* 向输出流存储模型 */
virtual void save(std::ostream& out) = 0;
/* 从输入流加载模型
* FIXME: 这个方法我做成了工厂模式
*/
//virtual void load(std::istream& in) = 0;
protected:
/* 获取函数对象 - 获取激活函数 */
static f_t getf(ActivationFunc type);
/* 获取函数对象 - 求激活函数导数 */
static f_t getfdot(ActivationFunc type);
/* ReLU 激活函数 */
static float relu(float x);
/* ReLU 导数 */
static float relu_derivative(float x);
/*
* leaky ReLU 激活函数
*
* NOTE: alpha太小或者选择使用ReLU函数的情况下,会导致梯度消失
* NOTE: Leaky ReLU 不会使梯度完全消失,但也会使它变得很小
*/
static float leaky_relu(float x);
/* leaky ReLU 导数 */
static float leaky_relu_derivative(float x);
/* Sigmoid 激活函数 */
static float sigmoid(float x);
/* Sigmoid 导数 */
static float sigmoid_derivative(float x);
/* Tanh 激活函数 */
static float tanh(float x);
/* Tanh 导数 */
static float tanh_derivative(float x);
};
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "../Dashun.h"
class ILayerConvolutional: public ILayer, public ILearnable {};
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "nn/ILayer.hpp"
#include "nn/ILearnable.hpp"
class ILayerFullyConnectted: public ILayer, public ILearnable {
public:
/* 正向传播,返回神经网络输出 */
virtual std::vector<float> forward(const std::vector<float>& inputs) = 0;
/* 反向传播,返回上一层的误差,方便堆叠 */
virtual std::vector<float> backward(const std::vector<float>& error) = 0;
};
+5
View File
@@ -0,0 +1,5 @@
#pragma once
class ILearnable {
public:
virtual void update(float discount) = 0;
};
+523
View File
@@ -0,0 +1,523 @@
#pragma once
#define cimg_use_png /* -lpng */
#define cimg_use_jpeg /* -ljpeg */
#define cimg_display 0 // 禁用显示窗口,用于服务器环境
#include "CImg.h"
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <cmath>
#include <memory>
#include <sys/stat.h> // 用于检查文件是否存在
#include <dirent.h> // 用于目录遍历
class ImageLoader {
private:
// 图像参数
int target_channels; // 目标通道数(如RGB=3, 灰度=1)
int target_height; // 目标高度
int target_width; // 目标宽度
bool normalize_to_01; // 是否归一化到[0,1]
bool normalize_mean_std; // 是否使用均值标准差归一化
float mean_val; // 均值(归一化用)
float std_val; // 标准差(归一化用)
bool auto_mean_std; // 是否自动计算均值标准差
// 预计算的均值和标准差(针对每个通道)
std::vector<float> channel_means;
std::vector<float> channel_stds;
// 用于调试的verbose标志
bool verbose;
// 辅助函数:检查文件是否存在(POSIX兼容)
bool file_exists(const std::string& filename) {
struct stat buffer;
return (stat(filename.c_str(), &buffer) == 0);
}
// 辅助函数:检查是否为普通文件
bool is_regular_file(const std::string& path) {
struct stat path_stat;
if (stat(path.c_str(), &path_stat) != 0) {
return false;
}
return S_ISREG(path_stat.st_mode);
}
// 辅助函数:获取文件扩展名
std::string get_file_extension(const std::string& filename) {
size_t dot_pos = filename.find_last_of(".");
if (dot_pos == std::string::npos) {
return "";
}
return filename.substr(dot_pos);
}
// 辅助函数:转换为小写
std::string to_lower(const std::string& str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
return result;
}
public:
// 构造函数
ImageLoader(int channels = 3, int height = 224, int width = 224,
bool norm_01 = true, bool norm_mean_std = false,
float mean = 0.0f, float std = 1.0f, bool auto_compute = false)
: target_channels(channels),
target_height(height),
target_width(width),
normalize_to_01(norm_01),
normalize_mean_std(norm_mean_std),
mean_val(mean),
std_val(std),
auto_mean_std(auto_compute),
verbose(false) {
// 初始化通道统计量
channel_means.resize(target_channels, 0.0f);
channel_stds.resize(target_channels, 1.0f);
}
// 从文件加载单张图片
std::vector<float> load_image(const std::string& filepath, bool verbose = false) {
this->verbose = verbose;
try {
// 检查文件是否存在
if (!file_exists(filepath)) {
throw std::runtime_error("File not found: " + filepath);
}
// 加载图像
cimg_library::CImg<unsigned char> img(filepath.c_str());
if (verbose) {
std::cout << "Loaded image: " << filepath << std::endl;
std::cout << "Original size: " << img.width() << "x" << img.height()
<< "x" << img.spectrum() << std::endl;
}
// 转换为目标格式并预处理
return preprocess_image(img, verbose);
} catch (const std::exception& e) {
std::cerr << "Error loading image " << filepath << ": " << e.what() << std::endl;
return {};
}
}
// 从CImg对象加载图片
std::vector<float> load_from_cimg(const cimg_library::CImg<unsigned char>& input_img, bool verbose = false) {
this->verbose = verbose;
try {
// 创建图像的副本(因为预处理函数可能会修改图像)
cimg_library::CImg<unsigned char> img = input_img;
if (verbose) {
std::cout << "Loading from CImg object" << std::endl;
std::cout << "Original size: " << img.width() << "x" << img.height()
<< "x" << img.spectrum() << std::endl;
}
// 检查图像是否为空
if (img.is_empty()) {
throw std::runtime_error("Input CImg object is empty");
}
// 转换为目标格式并预处理
return preprocess_image(img, verbose);
} catch (const std::exception& e) {
std::cerr << "Error loading image from CImg object: " << e.what() << std::endl;
return {};
}
}
// 从CImg对象加载图片(移动语义版本)
std::vector<float> load_from_cimg(cimg_library::CImg<unsigned char>&& input_img, bool verbose = false) {
this->verbose = verbose;
try {
// 直接使用移动过来的图像对象
cimg_library::CImg<unsigned char> img = std::move(input_img);
if (verbose) {
std::cout << "Loading from CImg object (move semantics)" << std::endl;
std::cout << "Original size: " << img.width() << "x" << img.height()
<< "x" << img.spectrum() << std::endl;
}
// 检查图像是否为空
if (img.is_empty()) {
throw std::runtime_error("Input CImg object is empty");
}
// 转换为目标格式并预处理
return preprocess_image(img, verbose);
} catch (const std::exception& e) {
std::cerr << "Error loading image from CImg object: " << e.what() << std::endl;
return {};
}
}
// 批量加载图片
std::vector<float> load_batch(const std::vector<std::string>& filepaths, bool verbose = false) {
this->verbose = verbose;
std::vector<float> batch_data;
int loaded_count = 0;
for (const auto& filepath : filepaths) {
auto img_data = load_image(filepath, verbose);
if (!img_data.empty()) {
if (batch_data.empty()) {
batch_data = img_data;
} else {
// 追加到batch数据中
batch_data.insert(batch_data.end(), img_data.begin(), img_data.end());
}
loaded_count++;
}
}
if (verbose) {
std::cout << "Successfully loaded " << loaded_count << " images out of "
<< filepaths.size() << std::endl;
}
return batch_data;
}
// 从文件夹加载所有图片
std::vector<float> load_from_directory(const std::string& dir_path,
const std::vector<std::string>& extensions = {".jpg", ".jpeg", ".png", ".bmp"},
bool verbose = false) {
this->verbose = verbose;
std::vector<std::string> image_files;
try {
// 打开目录
DIR* dir = opendir(dir_path.c_str());
if (!dir) {
throw std::runtime_error("Cannot open directory: " + dir_path);
}
// 读取目录项
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
std::string filename = entry->d_name;
// 跳过"."和".."
if (filename == "." || filename == "..") {
continue;
}
// 构建完整路径
std::string full_path = dir_path;
if (dir_path.back() != '/') {
full_path += "/";
}
full_path += filename;
// 检查是否为普通文件
if (is_regular_file(full_path)) {
// 检查文件扩展名
std::string ext = get_file_extension(filename);
std::string ext_lower = to_lower(ext);
// 检查是否在允许的扩展名列表中
bool valid_extension = false;
for (const auto& allowed_ext : extensions) {
if (ext_lower == to_lower(allowed_ext)) {
valid_extension = true;
break;
}
}
if (valid_extension) {
image_files.push_back(full_path);
}
}
}
closedir(dir);
if (verbose) {
std::cout << "Found " << image_files.size() << " images in directory: "
<< dir_path << std::endl;
}
return load_batch(image_files, verbose);
} catch (const std::exception& e) {
std::cerr << "Error reading directory " << dir_path << ": " << e.what() << std::endl;
return {};
}
}
// 计算数据集统计量(用于归一化)
void compute_dataset_statistics(const std::vector<std::string>& filepaths,
int max_images = 1000) {
std::cout << "Computing dataset statistics..." << std::endl;
int num_images = std::min(static_cast<int>(filepaths.size()), max_images);
std::vector<std::vector<float>> channel_sums(target_channels, std::vector<float>());
// 采样计算
for (int i = 0; i < num_images; i++) {
auto img_data = load_image(filepaths[i], false);
if (!img_data.empty()) {
// 计算每个通道的像素值
int pixels_per_channel = target_height * target_width;
for (int c = 0; c < target_channels; c++) {
const float* channel_start = img_data.data() + c * pixels_per_channel;
channel_sums[c].insert(channel_sums[c].end(),
channel_start,
channel_start + pixels_per_channel);
}
}
if (i % 100 == 0) {
std::cout << "Processed " << i << " images..." << std::endl;
}
}
// 计算均值和标准差
for (int c = 0; c < target_channels; c++) {
if (!channel_sums[c].empty()) {
// 计算均值
float sum = std::accumulate(channel_sums[c].begin(), channel_sums[c].end(), 0.0f);
channel_means[c] = sum / channel_sums[c].size();
// 计算标准差
float variance = 0.0f;
for (float val : channel_sums[c]) {
variance += (val - channel_means[c]) * (val - channel_means[c]);
}
variance /= channel_sums[c].size();
channel_stds[c] = std::sqrt(variance);
std::cout << "Channel " << c << ": mean = " << channel_means[c]
<< ", std = " << channel_stds[c] << std::endl;
}
}
std::cout << "Statistics computation completed." << std::endl;
}
// 获取批次数(根据总像素数计算)
int get_batch_size(const std::vector<float>& data) const {
int single_image_size = target_channels * target_height * target_width;
if (single_image_size == 0) return 0;
return data.size() / single_image_size;
}
// 设置归一化参数
void set_normalization_params(const std::vector<float>& means,
const std::vector<float>& stds) {
if (means.size() == target_channels && stds.size() == target_channels) {
channel_means = means;
channel_stds = stds;
} else {
std::cerr << "Error: Means and stds must have size " << target_channels << std::endl;
}
}
// 获取归一化参数
std::pair<std::vector<float>, std::vector<float>> get_normalization_params() const {
return {channel_means, channel_stds};
}
// 显示图像信息(用于调试)
void display_image_info(const std::vector<float>& image_data, int channels_to_show = 3) const {
int single_image_size = target_channels * target_height * target_width;
std::cout << "Image data info:" << std::endl;
std::cout << " Dimensions: " << target_channels << " x " << target_height
<< " x " << target_width << std::endl;
std::cout << " Total values: " << image_data.size() << std::endl;
if (!image_data.empty()) {
// 计算统计信息
float min_val = *std::min_element(image_data.begin(), image_data.end());
float max_val = *std::max_element(image_data.begin(), image_data.end());
float sum_val = std::accumulate(image_data.begin(), image_data.end(), 0.0f);
float mean_val = sum_val / image_data.size();
std::cout << " Min value: " << min_val << std::endl;
std::cout << " Max value: " << max_val << std::endl;
std::cout << " Mean value: " << mean_val << std::endl;
// 显示每个通道的前几个像素
int pixels_per_channel = target_height * target_width;
for (int c = 0; c < std::min(target_channels, channels_to_show); c++) {
std::cout << " Channel " << c << " (first 5 pixels): ";
for (int i = 0; i < std::min(5, pixels_per_channel); i++) {
std::cout << image_data[c * pixels_per_channel + i] << " ";
}
std::cout << std::endl;
}
}
}
// 获取目标尺寸(公开访问器)
int get_target_height() const { return target_height; }
int get_target_width() const { return target_width; }
int get_target_channels() const { return target_channels; }
// 设置verbose模式
void set_verbose(bool v) { verbose = v; }
private:
// 图像预处理主函数
std::vector<float> preprocess_image(cimg_library::CImg<unsigned char>& img, bool verbose = false) {
// 1. 调整通道数
adjust_channels(img);
// 2. 调整尺寸
resize_image(img);
// 3. 转换为浮点数
std::vector<float> float_data = convert_to_float(img);
// 4. 重新排列维度为 [channels, height, width]
std::vector<float> reordered_data = reorder_dimensions(float_data, img);
// 5. 归一化
if (normalize_to_01) {
normalize_0_1(reordered_data);
}
if (normalize_mean_std) {
normalize_mean_std_channels(reordered_data);
}
if (verbose) {
std::cout << "Processed image size: " << target_channels << "x"
<< target_height << "x" << target_width << std::endl;
}
return reordered_data;
}
// 调整通道数
void adjust_channels(cimg_library::CImg<unsigned char>& img) {
int current_channels = img.spectrum();
if (current_channels == 1 && target_channels == 3) {
// 灰度转RGB:复制单通道到三个通道
cimg_library::CImg<unsigned char> rgb_img(img.width(), img.height(), img.depth(), 3);
cimg_forXYZ(img, x, y, z) {
rgb_img(x, y, z, 0) = img(x, y, z, 0); // R
rgb_img(x, y, z, 1) = img(x, y, z, 0); // G
rgb_img(x, y, z, 2) = img(x, y, z, 0); // B
}
img = rgb_img;
} else if (current_channels == 3 && target_channels == 1) {
// RGB转灰度:使用标准公式
img = img.get_channel(0) * 0.299f +
img.get_channel(1) * 0.587f +
img.get_channel(2) * 0.114f;
} else if (current_channels == 4 && target_channels == 3) {
// RGBA转RGB:移除alpha通道
img.channels(0, 2);
} else if (current_channels > target_channels) {
// 保留前target_channels个通道
img.channels(0, target_channels - 1);
} else if (current_channels < target_channels) {
// 重复最后一个通道来补足
int channels_to_add = target_channels - current_channels;
for (int i = 0; i < channels_to_add; i++) {
img.append(img.get_channel(current_channels - 1), 'c');
}
}
}
// 调整图像尺寸
void resize_image(cimg_library::CImg<unsigned char>& img) {
if (img.width() != target_width || img.height() != target_height) {
if (verbose) {
std::cout << "Resizing from " << img.width() << "x" << img.height()
<< " to " << target_width << "x" << target_height << std::endl;
}
// 使用CImg的resize函数
img.resize(target_width, target_height, -100, -100, 3); // 3 = 三次样条插值
}
}
// 转换为浮点数
std::vector<float> convert_to_float(const cimg_library::CImg<unsigned char>& img) {
std::vector<float> float_data;
float_data.reserve(img.width() * img.height() * img.spectrum());
cimg_forC(img, c) {
cimg_forXY(img, x, y) {
float_data.push_back(static_cast<float>(img(x, y, 0, c)));
}
}
return float_data;
}
// 重新排列维度为 [channels, height, width]
std::vector<float> reorder_dimensions(const std::vector<float>& data,
const cimg_library::CImg<unsigned char>& img) {
int channels = img.spectrum();
int height = img.height();
int width = img.width();
std::vector<float> reordered(channels * height * width);
// CImg默认布局: [width, height, depth, spectrum]
// 我们需要: [channels, height, width]
for (int c = 0; c < channels; c++) {
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
// CImg索引: (x, y, z, c) = (w, h, 0, c)
int src_idx = c * (height * width) + h * width + w;
int dst_idx = c * (height * width) + h * width + w;
reordered[dst_idx] = data[src_idx];
}
}
}
return reordered;
}
// 归一化到[0,1]
void normalize_0_1(std::vector<float>& data) {
for (auto& val : data) {
val /= 255.0f;
}
}
// 使用均值标准差归一化(逐通道)
void normalize_mean_std_channels(std::vector<float>& data) {
int pixels_per_channel = target_height * target_width;
for (int c = 0; c < target_channels; c++) {
float mean = auto_mean_std ? channel_means[c] : mean_val;
float std = auto_mean_std ? channel_stds[c] : std_val;
for (int i = 0; i < pixels_per_channel; i++) {
int idx = c * pixels_per_channel + i;
data[idx] = (data[idx] - mean) / std;
}
}
}
};
+64
View File
@@ -0,0 +1,64 @@
/*
___col___
/ \
+---------+ \
| + + + + | | row - 第一维度
| + + + + | |
+---------+ /
*/
#pragma once
#include <cmath>
#include <vector>
#include <iostream>
#include <stdexcept>
#include "cn/中文化.hpp"
命名空间 qing {
模板 <类型名 T>
/* 矩阵类 - 二维向量 (FIXME: 1.增加函数广播方法。2.增加+=操作符等)。3. 增加初始化方法*/
类 Matrx {
公开的:
Matrx();
Matrx(常量 长型 row, 常量 长型 col); /* 该构造函数用于从头开始构造 */
Matrx(常量 长型 row, 常量 长型 col, 常量 std::vector<T>& src); /* 该构造函数用于通过已有的vector进行构造 */
const T& get(long row, long col) const; /* 访问器 - 访问矩阵元素 */
T& witch(long row, long col); /* 访问器 - 访问矩阵元素 */
const std::vector<T>& get_data() const; /* 访问器 - 用于获取原始数据 */
const long get_col() const; /* 获取列数 */
const long get_row() const; /* 获取行数 */
bool has_same_shape(const Matrx<T>& other); /* 两个矩阵是否具有相同形状 */
bool has_dot_shape(const Matrx<T>& other); /* 具有矩阵乘法的形状(标准:A的列数与B的行数相等) */
void assert_same_shape(const Matrx<T>& other); /* 断言两个矩阵具有相同形状 */
void assert_dot_shape(const Matrx<T>& other); /* 断言两个矩阵具有点积前置形状 */
Matrx<T> operator+(const Matrx<T>& other); /* 加法操作符重定义 - 矩阵加法 */
Matrx<T> operator+(T& item); /* 矩阵加法广播 */
Matrx<T> operator-(const Matrx<T>& other); /* 操作符重载 - 矩阵减法 */
Matrx<T> operator-(T& item); /* 操作符重载 - 矩阵减法广播 */
const Matrx<T> operator*(const Matrx<T>& other); /* 操作符重载 - 矩阵乘法 */
Matrx<T> operator*(T& item); /* 操作符重载 - 矩阵乘法广播 */
Matrx<T> operator/(T& item); /* 操作符重载 - 矩阵除法广播 */
/* 储存矩阵 */
void save(std::ostream& out) {
out << row << " " << col; /* 储存形状 */
for (const auto& item: arr) { /* 储存数据 */
out << " ";
out << item;
}
}
/* FIXME: 不写load()方法的原因是使用工厂模式较为方便 */
/* FIXME: 增加矩阵转置操作 */
/* FIXME: 添加广播以使得矩阵与向量相乘 */
/* FIXME: 增加元素对应乘积 */
private:
long row, col;
std::vector<T> arr; /* 存储矩阵的张量 */
};
}
+199
View File
@@ -0,0 +1,199 @@
/*
* MatrixCuda.cuh - CUDA加速的矩阵运算库
*
* 基于现有的Matrx类设计,提供GPU加速的矩阵运算
* 支持float和double类型的矩阵运算
*
* 注意:使用CUDA需要安装NVIDIA CUDA Toolkit
*/
#pragma once
#ifdef __CUDACC__
#define CUDA_HOST_DEVICE __host__ __device__
#define CUDA_HOST __host__
#define CUDA_DEVICE __device__
#else
#define CUDA_HOST_DEVICE
#define CUDA_HOST
#define CUDA_DEVICE
#endif
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <iostream>
#include <vector>
#include <stdexcept>
#include <cstring>
namespace qing {
// 前向声明
template <typename T>
class MatrixCuda;
// CUDA内核函数声明
namespace cuda_kernels {
// 矩阵加法内核
template <typename T>
__global__ void matrix_add_kernel(const T* A, const T* B, T* C,
long rows, long cols, long total_elements);
// 矩阵减法内核
template <typename T>
__global__ void matrix_sub_kernel(const T* A, const T* B, T* C,
long rows, long cols, long total_elements);
// 矩阵标量乘法内核
template <typename T>
__global__ void matrix_scalar_mul_kernel(const T* A, T scalar, T* C,
long total_elements);
// 矩阵标量加法内核
template <typename T>
__global__ void matrix_scalar_add_kernel(const T* A, T scalar, T* C,
long total_elements);
// 矩阵转置内核
template <typename T>
__global__ void matrix_transpose_kernel(const T* A, T* C,
long rows, long cols);
// 矩阵元素对应乘积内核
template <typename T>
__global__ void matrix_hadamard_kernel(const T* A, const T* B, T* C,
long total_elements);
// 矩阵复制内核
template <typename T>
__global__ void matrix_copy_kernel(const T* src, T* dst, long total_elements);
} // namespace cuda_kernels
/**
* @brief CUDA加速的矩阵类
*
* 这个类提供了GPU加速的矩阵运算,包括:
* - 矩阵加法、减法
* - 标量运算
* - 矩阵转置
* - 元素对应乘积
* - 矩阵乘法(使用cuBLAS)
*/
template <typename T>
class MatrixCuda {
public:
// 构造函数和析构函数
CUDA_HOST MatrixCuda();
CUDA_HOST MatrixCuda(long rows, long cols);
CUDA_HOST MatrixCuda(long rows, long cols, const std::vector<T>& data);
CUDA_HOST MatrixCuda(const MatrixCuda<T>& other);
CUDA_HOST MatrixCuda(MatrixCuda<T>&& other) noexcept;
CUDA_HOST ~MatrixCuda();
// 赋值运算符
CUDA_HOST MatrixCuda<T>& operator=(const MatrixCuda<T>& other);
CUDA_HOST MatrixCuda<T>& operator=(MatrixCuda<T>&& other) noexcept;
// 内存管理
CUDA_HOST void allocate_device_memory();
CUDA_HOST void free_device_memory();
CUDA_HOST void copy_to_device();
CUDA_HOST void copy_from_device();
CUDA_HOST void sync_device_to_host();
CUDA_HOST void sync_host_to_device();
// 访问器
CUDA_HOST_DEVICE long get_rows() const { return rows_; }
CUDA_HOST_DEVICE long get_cols() const { return cols_; }
CUDA_HOST_DEVICE long get_total_elements() const { return rows_ * cols_; }
CUDA_HOST const std::vector<T>& get_host_data() const { return host_data_; }
CUDA_HOST T* get_device_data() const { return device_data_; }
CUDA_HOST T get_element(long row, long col) const;
CUDA_HOST void set_element(long row, long col, T value);
// 形状检查
CUDA_HOST bool has_same_shape(const MatrixCuda<T>& other) const;
CUDA_HOST bool has_dot_shape(const MatrixCuda<T>& other) const;
CUDA_HOST void assert_same_shape(const MatrixCuda<T>& other) const;
CUDA_HOST void assert_dot_shape(const MatrixCuda<T>& other) const;
// 矩阵运算(GPU加速)
CUDA_HOST MatrixCuda<T> add(const MatrixCuda<T>& other) const;
CUDA_HOST MatrixCuda<T> subtract(const MatrixCuda<T>& other) const;
CUDA_HOST MatrixCuda<T> multiply_scalar(T scalar) const;
CUDA_HOST MatrixCuda<T> add_scalar(T scalar) const;
CUDA_HOST MatrixCuda<T> transpose() const;
CUDA_HOST MatrixCuda<T> hadamard_product(const MatrixCuda<T>& other) const;
// 运算符重载
CUDA_HOST MatrixCuda<T> operator+(const MatrixCuda<T>& other) const;
CUDA_HOST MatrixCuda<T> operator+(T scalar) const;
CUDA_HOST MatrixCuda<T> operator-(const MatrixCuda<T>& other) const;
CUDA_HOST MatrixCuda<T> operator-(T scalar) const;
CUDA_HOST MatrixCuda<T> operator*(T scalar) const;
CUDA_HOST MatrixCuda<T> operator/(T scalar) const;
// 复合赋值运算符
CUDA_HOST MatrixCuda<T>& operator+=(const MatrixCuda<T>& other);
CUDA_HOST MatrixCuda<T>& operator+=(T scalar);
CUDA_HOST MatrixCuda<T>& operator-=(const MatrixCuda<T>& other);
CUDA_HOST MatrixCuda<T>& operator-=(T scalar);
CUDA_HOST MatrixCuda<T>& operator*=(T scalar);
CUDA_HOST MatrixCuda<T>& operator/=(T scalar);
// 矩阵乘法(使用cuBLAS)
CUDA_HOST MatrixCuda<T> matmul(const MatrixCuda<T>& other) const;
CUDA_HOST MatrixCuda<T> operator*(const MatrixCuda<T>& other) const;
// 工具函数
CUDA_HOST void print(const std::string& name = "") const;
CUDA_HOST void fill(T value);
CUDA_HOST void fill_random(T min = 0, T max = 1);
CUDA_HOST bool is_valid() const;
// 静态工厂方法
CUDA_HOST static MatrixCuda<T> zeros(long rows, long cols);
CUDA_HOST static MatrixCuda<T> ones(long rows, long cols);
CUDA_HOST static MatrixCuda<T> identity(long size);
CUDA_HOST static MatrixCuda<T> random(long rows, long cols, T min = 0, T max = 1);
// 从现有Matrx类转换
CUDA_HOST static MatrixCuda<T> from_matrx(const class Matrx<T>& mat);
CUDA_HOST class Matrx<T> to_matrx() const;
private:
long rows_;
long cols_;
std::vector<T> host_data_; // 主机内存数据
T* device_data_; // 设备内存数据
bool device_allocated_; // 设备内存是否已分配
bool host_modified_; // 主机数据是否已修改
bool device_modified_; // 设备数据是否已修改
// 私有辅助函数
CUDA_HOST void check_index(long row, long col) const;
CUDA_HOST void initialize_from_vector(const std::vector<T>& data);
// CUDA错误检查
CUDA_HOST static void check_cuda_error(cudaError_t error, const char* file, int line);
// 计算线程块和网格大小
CUDA_HOST static dim3 get_block_size(long total_elements);
CUDA_HOST static dim3 get_grid_size(long total_elements, dim3 block_size);
};
// CUDA错误检查宏
#define CHECK_CUDA_ERROR(err) (MatrixCuda<float>::check_cuda_error(err, __FILE__, __LINE__))
// 显式实例化声明
extern template class MatrixCuda<float>;
extern template class MatrixCuda<double>;
} // namespace qing
// 包含内联实现
#include "MatrixCuda.inl.cuh"
+739
View File
@@ -0,0 +1,739 @@
/*
* MatrixCuda.inl.cuh - MatrixCuda类的内联实现
* 包含CUDA内核函数和模板成员函数的实现
*/
#ifndef MATRIX_CUDA_INL_CUH
#define MATRIX_CUDA_INL_CUH
#include "MatrixCuda.cuh"
#include <random>
#include <chrono>
#include <algorithm>
namespace qing {
// ============================================================================
// CUDA内核函数实现
// ============================================================================
namespace cuda_kernels {
// 矩阵加法内核
template <typename T>
__global__ void matrix_add_kernel(const T* A, const T* B, T* C,
long rows, long cols, long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
C[i] = A[i] + B[i];
}
}
// 矩阵减法内核
template <typename T>
__global__ void matrix_sub_kernel(const T* A, const T* B, T* C,
long rows, long cols, long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
C[i] = A[i] - B[i];
}
}
// 矩阵标量乘法内核
template <typename T>
__global__ void matrix_scalar_mul_kernel(const T* A, T scalar, T* C,
long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
C[i] = A[i] * scalar;
}
}
// 矩阵标量加法内核
template <typename T>
__global__ void matrix_scalar_add_kernel(const T* A, T scalar, T* C,
long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
C[i] = A[i] + scalar;
}
}
// 矩阵转置内核
template <typename T>
__global__ void matrix_transpose_kernel(const T* A, T* C,
long rows, long cols) {
long row = blockIdx.y * blockDim.y + threadIdx.y;
long col = blockIdx.x * blockDim.x + threadIdx.x;
if (row < rows && col < cols) {
// A[row, col] -> C[col, row]
C[col * rows + row] = A[row * cols + col];
}
}
// 矩阵元素对应乘积内核
template <typename T>
__global__ void matrix_hadamard_kernel(const T* A, const T* B, T* C,
long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
C[i] = A[i] * B[i];
}
}
// 矩阵复制内核
template <typename T>
__global__ void matrix_copy_kernel(const T* src, T* dst, long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
dst[i] = src[i];
}
}
} // namespace cuda_kernels
// ============================================================================
// MatrixCuda类成员函数实现
// ============================================================================
// 构造函数和析构函数
template <typename T>
CUDA_HOST MatrixCuda<T>::MatrixCuda()
: rows_(0), cols_(0), device_data_(nullptr),
device_allocated_(false), host_modified_(false), device_modified_(false) {}
template <typename T>
CUDA_HOST MatrixCuda<T>::MatrixCuda(long rows, long cols)
: rows_(rows), cols_(cols), device_data_(nullptr),
device_allocated_(false), host_modified_(false), device_modified_(false) {
if (rows <= 0 || cols <= 0) {
throw std::invalid_argument("Matrix dimensions must be positive");
}
host_data_.resize(rows * cols, T(0));
}
template <typename T>
CUDA_HOST MatrixCuda<T>::MatrixCuda(long rows, long cols, const std::vector<T>& data)
: rows_(rows), cols_(cols), device_data_(nullptr),
device_allocated_(false), host_modified_(false), device_modified_(false) {
initialize_from_vector(data);
}
template <typename T>
CUDA_HOST MatrixCuda<T>::MatrixCuda(const MatrixCuda<T>& other)
: rows_(other.rows_), cols_(other.cols_),
host_data_(other.host_data_), device_data_(nullptr),
device_allocated_(false), host_modified_(other.host_modified_),
device_modified_(false) {
if (other.device_allocated_) {
allocate_device_memory();
copy_to_device();
}
}
template <typename T>
CUDA_HOST MatrixCuda<T>::MatrixCuda(MatrixCuda<T>&& other) noexcept
: rows_(other.rows_), cols_(other.cols_),
host_data_(std::move(other.host_data_)),
device_data_(other.device_data_),
device_allocated_(other.device_allocated_),
host_modified_(other.host_modified_),
device_modified_(other.device_modified_) {
other.rows_ = 0;
other.cols_ = 0;
other.device_data_ = nullptr;
other.device_allocated_ = false;
other.host_modified_ = false;
other.device_modified_ = false;
}
template <typename T>
CUDA_HOST MatrixCuda<T>::~MatrixCuda() {
free_device_memory();
}
// 赋值运算符
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator=(const MatrixCuda<T>& other) {
if (this != &other) {
free_device_memory();
rows_ = other.rows_;
cols_ = other.cols_;
host_data_ = other.host_data_;
host_modified_ = other.host_modified_;
device_modified_ = false;
if (other.device_allocated_) {
allocate_device_memory();
copy_to_device();
}
}
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator=(MatrixCuda<T>&& other) noexcept {
if (this != &other) {
free_device_memory();
rows_ = other.rows_;
cols_ = other.cols_;
host_data_ = std::move(other.host_data_);
device_data_ = other.device_data_;
device_allocated_ = other.device_allocated_;
host_modified_ = other.host_modified_;
device_modified_ = other.device_modified_;
other.rows_ = 0;
other.cols_ = 0;
other.device_data_ = nullptr;
other.device_allocated_ = false;
other.host_modified_ = false;
other.device_modified_ = false;
}
return *this;
}
// 内存管理函数
template <typename T>
CUDA_HOST void MatrixCuda<T>::allocate_device_memory() {
if (device_allocated_ || get_total_elements() == 0) {
return;
}
cudaError_t err = cudaMalloc(&device_data_, get_total_elements() * sizeof(T));
CHECK_CUDA_ERROR(err);
device_allocated_ = true;
device_modified_ = false;
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::free_device_memory() {
if (device_allocated_ && device_data_ != nullptr) {
cudaError_t err = cudaFree(device_data_);
CHECK_CUDA_ERROR(err);
device_data_ = nullptr;
device_allocated_ = false;
device_modified_ = false;
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::copy_to_device() {
if (!device_allocated_) {
allocate_device_memory();
}
if (host_modified_ && get_total_elements() > 0) {
cudaError_t err = cudaMemcpy(device_data_, host_data_.data(),
get_total_elements() * sizeof(T),
cudaMemcpyHostToDevice);
CHECK_CUDA_ERROR(err);
host_modified_ = false;
device_modified_ = true;
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::copy_from_device() {
if (device_allocated_ && device_modified_ && get_total_elements() > 0) {
cudaError_t err = cudaMemcpy(host_data_.data(), device_data_,
get_total_elements() * sizeof(T),
cudaMemcpyDeviceToHost);
CHECK_CUDA_ERROR(err);
device_modified_ = false;
host_modified_ = true;
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::sync_device_to_host() {
copy_from_device();
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::sync_host_to_device() {
copy_to_device();
}
// 访问器函数
template <typename T>
CUDA_HOST T MatrixCuda<T>::get_element(long row, long col) const {
check_index(row, col);
return host_data_[row * cols_ + col];
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::set_element(long row, long col, T value) {
check_index(row, col);
host_data_[row * cols_ + col] = value;
host_modified_ = true;
}
// 形状检查函数
template <typename T>
CUDA_HOST bool MatrixCuda<T>::has_same_shape(const MatrixCuda<T>& other) const {
return rows_ == other.rows_ && cols_ == other.cols_;
}
template <typename T>
CUDA_HOST bool MatrixCuda<T>::has_dot_shape(const MatrixCuda<T>& other) const {
return cols_ == other.rows_;
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::assert_same_shape(const MatrixCuda<T>& other) const {
if (!has_same_shape(other)) {
throw std::invalid_argument("Matrices must have the same shape");
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::assert_dot_shape(const MatrixCuda<T>& other) const {
if (!has_dot_shape(other)) {
throw std::invalid_argument("Matrices cannot be multiplied: cols of first != rows of second");
}
}
// 矩阵运算函数
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::add(const MatrixCuda<T>& other) const {
assert_same_shape(other);
MatrixCuda<T> result(rows_, cols_);
// 确保数据在设备上
sync_host_to_device();
other.sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数
long total_elements = get_total_elements();
dim3 block_size = get_block_size(total_elements);
dim3 grid_size = get_grid_size(total_elements, block_size);
// 启动内核
cuda_kernels::matrix_add_kernel<T><<<grid_size, block_size>>>(
device_data_, other.device_data_, result.device_data_,
rows_, cols_, total_elements
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::subtract(const MatrixCuda<T>& other) const {
assert_same_shape(other);
MatrixCuda<T> result(rows_, cols_);
// 确保数据在设备上
sync_host_to_device();
other.sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数
long total_elements = get_total_elements();
dim3 block_size = get_block_size(total_elements);
dim3 grid_size = get_grid_size(total_elements, block_size);
// 启动内核
cuda_kernels::matrix_sub_kernel<T><<<grid_size, block_size>>>(
device_data_, other.device_data_, result.device_data_,
rows_, cols_, total_elements
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::multiply_scalar(T scalar) const {
MatrixCuda<T> result(rows_, cols_);
// 确保数据在设备上
sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数
long total_elements = get_total_elements();
dim3 block_size = get_block_size(total_elements);
dim3 grid_size = get_grid_size(total_elements, block_size);
// 启动内核
cuda_kernels::matrix_scalar_mul_kernel<T><<<grid_size, block_size>>>(
device_data_, scalar, result.device_data_, total_elements
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::add_scalar(T scalar) const {
MatrixCuda<T> result(rows_, cols_);
// 确保数据在设备上
sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数
long total_elements = get_total_elements();
dim3 block_size = get_block_size(total_elements);
dim3 grid_size = get_grid_size(total_elements, block_size);
// 启动内核
cuda_kernels::matrix_scalar_add_kernel<T><<<grid_size, block_size>>>(
device_data_, scalar, result.device_data_, total_elements
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::transpose() const {
MatrixCuda<T> result(cols_, rows_);
// 确保数据在设备上
sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数(2D网格用于转置)
dim3 block_size(16, 16);
dim3 grid_size((cols_ + block_size.x - 1) / block_size.x,
(rows_ + block_size.y - 1) / block_size.y);
// 启动转置内核
cuda_kernels::matrix_transpose_kernel<T><<<grid_size, block_size>>>(
device_data_, result.device_data_, rows_, cols_
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::hadamard_product(const MatrixCuda<T>& other) const {
assert_same_shape(other);
MatrixCuda<T> result(rows_, cols_);
// 确保数据在设备上
sync_host_to_device();
other.sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数
long total_elements = get_total_elements();
dim3 block_size = get_block_size(total_elements);
dim3 grid_size = get_grid_size(total_elements, block_size);
// 启动内核
cuda_kernels::matrix_hadamard_kernel<T><<<grid_size, block_size>>>(
device_data_, other.device_data_, result.device_data_, total_elements
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
// 运算符重载
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator+(const MatrixCuda<T>& other) const {
return add(other);
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator+(T scalar) const {
return add_scalar(scalar);
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator-(const MatrixCuda<T>& other) const {
return subtract(other);
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator-(T scalar) const {
return add_scalar(-scalar);
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator*(T scalar) const {
return multiply_scalar(scalar);
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator/(T scalar) const {
if (scalar == T(0)) {
throw std::invalid_argument("Division by zero");
}
return multiply_scalar(T(1) / scalar);
}
// 复合赋值运算符
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator+=(const MatrixCuda<T>& other) {
*this = *this + other;
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator+=(T scalar) {
*this = *this + scalar;
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator-=(const MatrixCuda<T>& other) {
*this = *this - other;
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator-=(T scalar) {
*this = *this - scalar;
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator*=(T scalar) {
*this = *this * scalar;
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator/=(T scalar) {
*this = *this / scalar;
return *this;
}
// 矩阵乘法(基础实现,使用朴素算法)
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::matmul(const MatrixCuda<T>& other) const {
assert_dot_shape(other);
MatrixCuda<T> result(rows_, other.cols_);
// 同步数据到主机(使用朴素算法在主机上计算)
sync_device_to_host();
other.sync_device_to_host();
// 朴素矩阵乘法(O(n³))
for (long i = 0; i < rows_; ++i) {
for (long j = 0; j < other.cols_; ++j) {
T sum = T(0);
for (long k = 0; k < cols_; ++k) {
sum += get_element(i, k) * other.get_element(k, j);
}
result.set_element(i, j, sum);
}
}
// 标记结果已修改
result.host_modified_ = true;
result.sync_host_to_device();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator*(const MatrixCuda<T>& other) const {
return matmul(other);
}
// 工具函数
template <typename T>
CUDA_HOST void MatrixCuda<T>::print(const std::string& name) const {
sync_device_to_host();
if (!name.empty()) {
std::cout << name << " (" << rows_ << "x" << cols_ << "):" << std::endl;
} else {
std::cout << "Matrix (" << rows_ << "x" << cols_ << "):" << std::endl;
}
for (long i = 0; i < rows_; ++i) {
std::cout << " [";
for (long j = 0; j < cols_; ++j) {
std::cout << get_element(i, j);
if (j < cols_ - 1) std::cout << ", ";
}
std::cout << "]" << std::endl;
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::fill(T value) {
std::fill(host_data_.begin(), host_data_.end(), value);
host_modified_ = true;
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::fill_random(T min, T max) {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<T> dist(min, max);
for (auto& val : host_data_) {
val = dist(gen);
}
host_modified_ = true;
}
template <typename T>
CUDA_HOST bool MatrixCuda<T>::is_valid() const {
return rows_ > 0 && cols_ > 0 && !host_data_.empty();
}
// 静态工厂方法
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::zeros(long rows, long cols) {
MatrixCuda<T> mat(rows, cols);
mat.fill(T(0));
return mat;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::ones(long rows, long cols) {
MatrixCuda<T> mat(rows, cols);
mat.fill(T(1));
return mat;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::identity(long size) {
MatrixCuda<T> mat(size, size);
mat.fill(T(0));
for (long i = 0; i < size; ++i) {
mat.set_element(i, i, T(1));
}
return mat;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::random(long rows, long cols, T min, T max) {
MatrixCuda<T> mat(rows, cols);
mat.fill_random(min, max);
return mat;
}
// 从现有Matrx类转换
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::from_matrx(const class Matrx<T>& mat) {
// 注意:这里假设Matrx类有get_row(), get_col(), get_data()方法
long rows = mat.get_row();
long cols = mat.get_col();
const std::vector<T>& data = mat.get_data();
return MatrixCuda<T>(rows, cols, data);
}
template <typename T>
CUDA_HOST class Matrx<T> MatrixCuda<T>::to_matrx() const {
sync_device_to_host();
// 注意:这里假设Matrx类有相应的构造函数
// 实际实现可能需要根据Matrx类的具体接口调整
return class Matrx<T>(rows_, cols_, host_data_);
}
// 私有辅助函数
template <typename T>
CUDA_HOST void MatrixCuda<T>::check_index(long row, long col) const {
if (row < 0 || row >= rows_ || col < 0 || col >= cols_) {
throw std::out_of_range("Matrix index out of range");
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::initialize_from_vector(const std::vector<T>& data) {
if (data.size() != static_cast<size_t>(rows_ * cols_)) {
throw std::invalid_argument("Data size does not match matrix dimensions");
}
host_data_ = data;
host_modified_ = true;
}
// CUDA错误检查
template <typename T>
CUDA_HOST void MatrixCuda<T>::check_cuda_error(cudaError_t error, const char* file, int line) {
if (error != cudaSuccess) {
std::cerr << "CUDA error at " << file << ":" << line << ": "
<< cudaGetErrorString(error) << std::endl;
throw std::runtime_error("CUDA error occurred");
}
}
// 计算线程块和网格大小
template <typename T>
CUDA_HOST dim3 MatrixCuda<T>::get_block_size(long total_elements) {
// 使用256个线程的块(CUDA的典型值)
const long max_threads_per_block = 256;
long threads = std::min(total_elements, max_threads_per_block);
return dim3(threads, 1, 1);
}
template <typename T>
CUDA_HOST dim3 MatrixCuda<T>::get_grid_size(long total_elements, dim3 block_size) {
// 计算需要的块数
long blocks = (total_elements + block_size.x - 1) / block_size.x;
// 限制最大块数(避免过多块)
const long max_blocks = 65535;
blocks = std::min(blocks, max_blocks);
return dim3(blocks, 1, 1);
}
// 显式实例化
template class MatrixCuda<float>;
template class MatrixCuda<double>;
} // namespace qing
#endif // MATRIX_CUDA_INL_CUH
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#include <iostream>
#include <stdexcept>
#include "nn/NeuralNetwork.hpp"
namespace qing {
class NNBuilder { /* 神经网络构建 */
public:
void add(NeuralNetwork &layer);
/* 获取变换层的形状
* FIXME: 这个函数有必要吗 */
std::vector<std::pair<long, long>> get_shape() const;
/* 向标准输出打印形状 */
void print_shape() const;
/* 向输出流储存模型 */
void save(std::ostream& out) {
for (auto& layer: nn) {
layer.save(out);
}
}
/* 从输入流加载模型 */
void load(std::istream& in) {
nn.clear();
while(true) {
try{
auto layer = NeuralNetwork::Load_in_Factory(in);
add(layer);
}
catch (std::runtime_error& e) {
break;
}
}
}
/*
* 将一层非线性层分裂为两层,相当于插入一层神经元
*/
//void fork(int index, long num, float rate) {
/*
* 神经网络生长,增加第layer层,每层增加num个神经元
*
* layer: 增加的层数
* num: 增加的神经元数量
*
* FIXME: 神经网络生长时应当冻结其它层(除了最后一层?)(learning_rate == 0)
*/
//void grow(int layer, int num) {
/* 前向反馈 */
std::vector<float> forward(std::vector<float>& r);
/* 反向传播 */
std::vector<float> backward(std::vector<float>& errs, float discount = 1.0);
/* 计算误差 */
std::vector<float> cal_err(std::vector<float>& r, std::vector<float>& t);
private:
std::vector<NeuralNetwork> nn;
};
}
+149
View File
@@ -0,0 +1,149 @@
#pragma once
#include <random>
#include "nn/Matrix.hh"
#include "nn/ILayerFullyConnectted.hpp"
namespace qing {
/* 人工神经网络 - 全连接层 */
class NeuralNetwork: public ILayerFullyConnectted {
public:
/* 构造函数 使用基本参数构造
* FIXME: 只能输入已经存在的向量 */
NeuralNetwork(
const long inputno, const long outputno,
const std::vector<float> &weights,
const std::vector<float> &bias,
const float learning_rate,
const ActivationFunc func_type
);
/* 获取激活函数枚举类型 */
const ActivationFunc& get_f() const {
return f_type;
}
/* 获取学习率 */
const float& get_lr () const {
return learning_rate;
}
/*
* 获取输入层大小
*
* FIXME: 优化掉这两个访问器,通过直接返回矩阵,获取层的形状
*/
long get_inputno() const;
/* 获取输出层大小 */
long get_outputno() const;
/* 获取权重矩阵 */
Matrx<float>& get_weights();
/* 获取偏置矩阵 */
Matrx<float>& get_bias();
/* 正态分布Xavier初始化 */
static void xavierNormalInit(std::vector<float> &weights, long fan_in, long fan_out);
/* 均匀分布Xavier初始化函数 */
static void xavierUniformInit(std::vector<float> &weights, int fan_in, int fan_out);
/* 工厂模式 通过形状构建全连接层 */
static NeuralNetwork Create_in_Factory(const long inputno, const long outputno, float learning_rate, ActivationFunc func_type);
/* 工厂模式 通过输入流读形状构建 */
static NeuralNetwork Load_in_Factory(std::istream& in) {
std::string f_name;
if (!(in >> f_name))
throw std::runtime_error("read out of lines");
float learning_rate;
in >> learning_rate;
/*------------------*/
size_t row, col;
in >> row; /* FIXME: 如果是负数会怎样 */
in >> col;
auto weis = load_vec(in, row * col);
size_t rowb, colb;
in >> rowb >> colb; /* FIXME: 如果这里读错了,也要抛出异常 */
auto bias = load_vec(in, col);
auto layer = NeuralNetwork(
row, col, weis, bias, learning_rate, ILayer::parse_ff_type(f_name));
/*------------------*/
return layer;
}
static std::vector<float> load_vec(std::istream& in, size_t length) {
std::vector<float> data;
for (size_t i = 0; i < length; ++i) {
float item;
in >> item;
data.push_back(item);
}
return data;
}
/* 前向反馈 */
std::vector<float> forward(const std::vector<float>& x) override;
/* 反向传播 */
std::vector<float> backward(const std::vector<float>& errors) override;
/*
* 更新权重
*
* 可以输入一个折扣系数
*/
void update(float discount = 1.0);
/*
* 神经网络层长大
*
* 重新初始化权重矩阵,使用均匀分布初始化
*
* num: 增加的神经元数量
* flag: true为增加输入层,false为增加输出层
*/
//void grow(int num, bool flag) {
/* 输出神经网络到输出流 */
void save(std::ostream& out) override {
out << get_f_type(f_type) << " ";
out << learning_rate << " ";
weights.save(out); out << " ";
bias.save(out); out << "\n";
}
/* 从输入流加载模型
* FIXME: 只能从工厂模式中构造 */
//void load(std::istream& in) override
private:
/* 神经网络层规格 */
long inputno, outputno;
/* 权重与偏置矩阵 */
Matrx<float> weights; /* (outputs, inputs) */
Matrx<float> bias; /* (1, outputs) */
/* 学习率 */
float learning_rate;
/* 激活函数 */
ActivationFunc f_type;
/* 存储在类内部的上一次的输入,输出,和激活前输出 */
Matrx<float> prev_inputs, prev_raw_inputs, prev_outputs;
/* 存储本次学习的权重梯度以及偏置梯度 */
Matrx<float> weights_grad, bias_grad;
};
}
+502
View File
@@ -0,0 +1,502 @@
#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <limits>
#include <functional>
class PoolingLayer {
private:
// 池化类型枚举
enum class PoolType {
MAX,
AVG
};
// 参数
int pool_height;
int pool_width;
int stride;
int padding;
PoolType pool_type;
// 输入输出尺寸
int input_channels;
int input_height;
int input_width;
int output_height;
int output_width;
// 中间变量缓存(用于反向传播)
std::vector<int> max_idx_cache; // 最大池化时缓存最大值位置
std::vector<float> input_cache; // 缓存的输入
std::vector<float> output_cache; // 缓存的输出
// 辅助函数
//void max_pool_forward(const float* input, float* output, int batch_size);
//void avg_pool_forward(const float* input, float* output, int batch_size);
//void max_pool_backward(const float* grad_output, float* grad_input, int batch_size);
//void avg_pool_backward(const float* grad_output, float* grad_input, int batch_size);
public:
// 构造函数
PoolingLayer(int pool_size, int stride = 2, int padding = 0, bool max_pool = true)
: pool_height(pool_size),
pool_width(pool_size),
stride(stride),
padding(padding),
pool_type(max_pool ? PoolType::MAX : PoolType::AVG) {}
// 带不同高度和宽度的构造函数
PoolingLayer(int pool_height, int pool_width, int stride = 2, int padding = 0, bool max_pool = true)
: pool_height(pool_height),
pool_width(pool_width),
stride(stride),
padding(padding),
pool_type(max_pool ? PoolType::MAX : PoolType::AVG) {}
// 设置输入尺寸
void set_input_shape(int channels, int height, int width) {
input_channels = channels;
input_height = height;
input_width = width;
// 计算输出尺寸
output_height = (height + 2 * padding - pool_height) / stride + 1;
output_width = (width + 2 * padding - pool_width) / stride + 1;
// 清空缓存
max_idx_cache.clear();
input_cache.clear();
output_cache.clear();
}
// 前向传播
std::vector<float> forward(const std::vector<float>& input, int batch_size = 1, bool training = true) {
// 计算输出大小
int output_size = batch_size * input_channels * output_height * output_width;
std::vector<float> output(output_size, 0.0f);
// 如果训练模式,缓存输入
if (training) {
input_cache = input;
}
// 根据池化类型选择前向传播函数
if (pool_type == PoolType::MAX) {
max_pool_forward(input.data(), output.data(), batch_size);
} else {
avg_pool_forward(input.data(), output.data(), batch_size);
}
// 如果训练模式,缓存输出
if (training) {
output_cache = output;
}
return output;
}
// 反向传播
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
// 创建输入梯度
std::vector<float> grad_input(batch_size * input_channels * input_height * input_width, 0.0f);
// 根据池化类型选择反向传播函数
if (pool_type == PoolType::MAX) {
max_pool_backward(grad_output.data(), grad_input.data(), batch_size);
} else {
avg_pool_backward(grad_output.data(), grad_input.data(), batch_size);
}
return grad_input;
}
// 获取输出尺寸
std::tuple<int, int, int> get_output_shape() const {
return std::make_tuple(input_channels, output_height, output_width);
}
// 获取参数信息
void print_info() const {
std::cout << "Pooling Layer Info:" << std::endl;
std::cout << " Type: " << (pool_type == PoolType::MAX ? "Max Pooling" : "Average Pooling") << std::endl;
std::cout << " Pool Size: " << pool_height << "x" << pool_width << std::endl;
std::cout << " Stride: " << stride << std::endl;
std::cout << " Padding: " << padding << std::endl;
std::cout << " Input Shape: " << input_channels << "x" << input_height << "x" << input_width << std::endl;
std::cout << " Output Shape: " << input_channels << "x" << output_height << "x" << output_width << std::endl;
}
private:
// 最大池化前向传播实现
void max_pool_forward(const float* input, float* output, int batch_size) {
// 计算填充后的尺寸
int padded_height = input_height + 2 * padding;
int padded_width = input_width + 2 * padding;
// 清空最大值位置缓存
max_idx_cache.clear();
max_idx_cache.resize(batch_size * input_channels * output_height * output_width, -1);
// 对每个批次、每个通道进行处理
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
// 当前通道的输入和输出指针
const float* input_ptr = input + b * input_channels * input_height * input_width +
c * input_height * input_width;
float* output_ptr = output + b * input_channels * output_height * output_width +
c * output_height * output_width;
int* max_idx_ptr = max_idx_cache.data() + b * input_channels * output_height * output_width +
c * output_height * output_width;
// 对每个输出位置进行计算
for (int oh = 0; oh < output_height; oh++) {
for (int ow = 0; ow < output_width; ow++) {
// 计算池化窗口的起始位置
int start_h = oh * stride - padding;
int start_w = ow * stride - padding;
// 计算池化窗口的结束位置
int end_h = std::min(start_h + pool_height, padded_height);
int end_w = std::min(start_w + pool_width, padded_width);
// 调整起始位置,确保不超出边界
start_h = std::max(start_h, 0);
start_w = std::max(start_w, 0);
// 在池化窗口内寻找最大值
float max_val = -std::numeric_limits<float>::max();
int max_idx = -1;
for (int ph = start_h; ph < end_h; ph++) {
for (int pw = start_w; pw < end_w; pw++) {
// 计算原始输入中的索引(考虑填充)
int orig_ph = ph - padding;
int orig_pw = pw - padding;
// 检查是否在有效范围内
if (orig_ph >= 0 && orig_ph < input_height &&
orig_pw >= 0 && orig_pw < input_width) {
int idx = orig_ph * input_width + orig_pw;
float val = input_ptr[idx];
if (val > max_val) {
max_val = val;
max_idx = idx;
}
}
}
}
// 存储输出和最大值位置
output_ptr[oh * output_width + ow] = max_val;
max_idx_ptr[oh * output_width + ow] = max_idx;
}
}
}
}
}
// 平均池化前向传播实现
void avg_pool_forward(const float* input, float* output, int batch_size) {
// 计算填充后的尺寸
int padded_height = input_height + 2 * padding;
int padded_width = input_width + 2 * padding;
// 对每个批次、每个通道进行处理
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
// 当前通道的输入和输出指针
const float* input_ptr = input + b * input_channels * input_height * input_width +
c * input_height * input_width;
float* output_ptr = output + b * input_channels * output_height * output_width +
c * output_height * output_width;
// 对每个输出位置进行计算
for (int oh = 0; oh < output_height; oh++) {
for (int ow = 0; ow < output_width; ow++) {
// 计算池化窗口的起始位置
int start_h = oh * stride - padding;
int start_w = ow * stride - padding;
// 计算池化窗口的结束位置
int end_h = std::min(start_h + pool_height, padded_height);
int end_w = std::min(start_w + pool_width, padded_width);
// 调整起始位置,确保不超出边界
start_h = std::max(start_h, 0);
start_w = std::max(start_w, 0);
// 在池化窗口内计算平均值
float sum = 0.0f;
int count = 0;
for (int ph = start_h; ph < end_h; ph++) {
for (int pw = start_w; pw < end_w; pw++) {
// 计算原始输入中的索引(考虑填充)
int orig_ph = ph - padding;
int orig_pw = pw - padding;
// 检查是否在有效范围内
if (orig_ph >= 0 && orig_ph < input_height &&
orig_pw >= 0 && orig_pw < input_width) {
int idx = orig_ph * input_width + orig_pw;
sum += input_ptr[idx];
count++;
}
}
}
// 计算平均值
output_ptr[oh * output_width + ow] = (count > 0) ? sum / count : 0.0f;
}
}
}
}
}
// 最大池化反向传播实现
void max_pool_backward(const float* grad_output, float* grad_input, int batch_size) {
// 初始化输入梯度为0
std::memset(grad_input, 0, batch_size * input_channels * input_height * input_width * sizeof(float));
// 对每个批次、每个通道进行处理
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
// 当前通道的梯度指针
const float* grad_output_ptr = grad_output + b * input_channels * output_height * output_width +
c * output_height * output_width;
float* grad_input_ptr = grad_input + b * input_channels * input_height * input_width +
c * input_height * input_width;
const int* max_idx_ptr = max_idx_cache.data() + b * input_channels * output_height * output_width +
c * output_height * output_width;
// 对每个输出位置进行反向传播
for (int oh = 0; oh < output_height; oh++) {
for (int ow = 0; ow < output_width; ow++) {
// 获取最大值位置索引
int max_idx = max_idx_ptr[oh * output_width + ow];
// 如果最大值位置有效,将梯度传递到该位置
if (max_idx >= 0) {
grad_input_ptr[max_idx] += grad_output_ptr[oh * output_width + ow];
}
}
}
}
}
}
// 平均池化反向传播实现
void avg_pool_backward(const float* grad_output, float* grad_input, int batch_size) {
// 初始化输入梯度为0
std::memset(grad_input, 0, batch_size * input_channels * input_height * input_width * sizeof(float));
// 计算填充后的尺寸
int padded_height = input_height + 2 * padding;
int padded_width = input_width + 2 * padding;
// 对每个批次、每个通道进行处理
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
// 当前通道的梯度指针
const float* grad_output_ptr = grad_output + b * input_channels * output_height * output_width +
c * output_height * output_width;
float* grad_input_ptr = grad_input + b * input_channels * input_height * input_width +
c * input_height * input_width;
// 对每个输出位置进行反向传播
for (int oh = 0; oh < output_height; oh++) {
for (int ow = 0; ow < output_width; ow++) {
// 计算池化窗口的起始位置
int start_h = oh * stride - padding;
int start_w = ow * stride - padding;
// 计算池化窗口的结束位置
int end_h = std::min(start_h + pool_height, padded_height);
int end_w = std::min(start_w + pool_width, padded_width);
// 调整起始位置,确保不超出边界
start_h = std::max(start_h, 0);
start_w = std::max(start_w, 0);
// 计算有效池化窗口的大小
int count = 0;
for (int ph = start_h; ph < end_h; ph++) {
for (int pw = start_w; pw < end_w; pw++) {
// 计算原始输入中的索引(考虑填充)
int orig_ph = ph - padding;
int orig_pw = pw - padding;
// 检查是否在有效范围内
if (orig_ph >= 0 && orig_ph < input_height &&
orig_pw >= 0 && orig_pw < input_width) {
count++;
}
}
}
// 如果池化窗口内有有效元素,计算平均梯度
if (count > 0) {
float avg_grad = grad_output_ptr[oh * output_width + ow] / count;
// 将平均梯度分配到池化窗口内的每个位置
for (int ph = start_h; ph < end_h; ph++) {
for (int pw = start_w; pw < end_w; pw++) {
// 计算原始输入中的索引(考虑填充)
int orig_ph = ph - padding;
int orig_pw = pw - padding;
// 检查是否在有效范围内
if (orig_ph >= 0 && orig_ph < input_height &&
orig_pw >= 0 && orig_pw < input_width) {
int idx = orig_ph * input_width + orig_pw;
grad_input_ptr[idx] += avg_grad;
}
}
}
}
}
}
}
}
}
};
// 使用示例函数
inline void pooling_layer_example() {
std::cout << "=== Pooling Layer Example ===" << std::endl;
// 创建最大池化层
PoolingLayer max_pool(2, 2, 0, true); // 2x2 最大池化,步长2,无填充
max_pool.set_input_shape(3, 4, 4); // 3个通道,4x4输入
// 创建输入数据
std::vector<float> input(3 * 4 * 4);
for (int i = 0; i < 3 * 4 * 4; i++) {
input[i] = static_cast<float>(i) / 10.0f;
}
// 前向传播
std::cout << "Forward pass..." << std::endl;
auto output = max_pool.forward(input, 1, true);
// 获取输出形状
auto [channels, height, width] = max_pool.get_output_shape();
std::cout << "Output shape: " << channels << "x" << height << "x" << width << std::endl;
// 打印输出
std::cout << "Output values (first 10): ";
for (int i = 0; i < std::min(10, static_cast<int>(output.size())); i++) {
std::cout << output[i] << " ";
}
std::cout << std::endl;
// 创建梯度(假设全为1)
std::vector<float> grad_output(output.size(), 1.0f);
// 反向传播
std::cout << "Backward pass..." << std::endl;
auto grad_input = max_pool.backward(grad_output, 1);
std::cout << "Gradient input size: " << grad_input.size() << std::endl;
std::cout << "Gradient input values (first 10): ";
for (int i = 0; i < std::min(10, static_cast<int>(grad_input.size())); i++) {
std::cout << grad_input[i] << " ";
}
std::cout << std::endl;
// 打印层信息
max_pool.print_info();
}
// 全局平均池化层(常用于CNN分类网络的最后)
class GlobalAveragePooling {
private:
int input_channels;
int input_height;
int input_width;
std::vector<float> input_cache;
public:
// 设置输入尺寸
void set_input_shape(int channels, int height, int width) {
input_channels = channels;
input_height = height;
input_width = width;
}
// 前向传播
std::vector<float> forward(const std::vector<float>& input, int batch_size = 1, bool training = true) {
// 缓存输入(如果需要)
if (training) {
input_cache = input;
}
// 计算输出大小:每个通道一个值
int output_size = batch_size * input_channels;
std::vector<float> output(output_size, 0.0f);
// 对每个批次、每个通道计算平均值
int spatial_size = input_height * input_width;
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
const float* input_ptr = input.data() + b * input_channels * spatial_size + c * spatial_size;
float sum = 0.0f;
// 计算该通道所有空间位置的平均值
for (int i = 0; i < spatial_size; i++) {
sum += input_ptr[i];
}
output[b * input_channels + c] = sum / spatial_size;
}
}
return output;
}
// 反向传播
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
// 创建输入梯度
int spatial_size = input_height * input_width;
std::vector<float> grad_input(batch_size * input_channels * spatial_size, 0.0f);
// 对每个批次、每个通道分配梯度
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
float grad = grad_output[b * input_channels + c] / spatial_size;
float* grad_input_ptr = grad_input.data() + b * input_channels * spatial_size + c * spatial_size;
// 将梯度均匀分配到所有空间位置
for (int i = 0; i < spatial_size; i++) {
grad_input_ptr[i] = grad;
}
}
}
return grad_input;
}
// 获取输出尺寸
std::tuple<int, int> get_output_shape() const {
return std::make_tuple(input_channels, 1);
}
};
/*
// 创建池化层
PoolingLayer pool(2, 2, 0, true); // 2x2最大池化,步长2,无填充
pool.set_input_shape(64, 28, 28); // 64通道,28x28输入
// 前向传播
auto output = pool.forward(input, batch_size, true);
// 反向传播
auto grad_input = pool.backward(grad_output, batch_size);
*/
+229
View File
@@ -0,0 +1,229 @@
#pragma once
#include <iostream>
#include <fstream>
#include <filesystem>
#include <vector>
#include <sys/stat.h>
namespace qing {
/* 子系统类 */
class Modules {
public:
/* 传入子系统的路径 然后验证是否存在 */
Modules(std::string& mods) {
this->home = mods;
if (!is_exist(this->home))
throw SubsystemNotExist(mods);
}
/*
* 获取子系统下某个模块的启动脚本路径
*
* NOTE: 防注入检查是必须的
*/
std::string GetInitp(std::string name) {
std::string ret = get_mod_init_path(name);
if (is_exist(ret)) {
return ret;
}
else {
throw InitScriptNotExist(ret);
}
}
/*
* 列出子系统下的所有模块
*
* 优化建议:做抽象层表示模块概念
*/
std::vector<std::string> ListAll() {
std::vector<std::string> folders;
try {
for (const auto& entry: std::filesystem::directory_iterator(this->home))
if (entry.is_directory())
folders.push_back(entry.path().filename().string());
}
catch(const std::filesystem::filesystem_error& e) {
ListModulesFailed(this->home, e.what());
}
return folders;
}
/* 在该子系统下创建一个初始模块 */
void CrtMod(std::string &name) {
crt_mod_dir(name);
init_mod_dir(name);
init_mod_man(name);
grd_priv(name);
}
/* 删除子系统下一个模块 */
void DelMod(std::string &name) {
std::string fullpath = get_mod_path(name);
deleteDirectory(fullpath);
}
/* 子系统异常模块 */
class Mods_Exp: public std::runtime_error {
public:
explicit Mods_Exp(const std::string& msg): std::runtime_error(msg) {
;
}
};
/* 子系统不存在异常 */
class SubsystemNotExist: public Mods_Exp {
public:
explicit SubsystemNotExist(const std::string &name): Mods_Exp("Target subsystem " + name + " not exists.") {
;
}
};
/* 启动脚本不存在异常 */
class InitScriptNotExist: public Mods_Exp {
public:
explicit InitScriptNotExist(const std::string &name): Mods_Exp("Target init script " + name + " not exists") {
;
}
};
/* 列举模块失败异常 */
class ListModulesFailed: public Mods_Exp {
public:
explicit ListModulesFailed(const std::string &name, const std::string &reson): Mods_Exp("Error when read subsystem <" + name + "> , because " + reson) {
;
}
};
/* 要创建的路径已经存在 */
class AlreadyExist: public Mods_Exp {
public:
explicit AlreadyExist(const std::string &name): Mods_Exp(name) {
;
}
};
/* 创建失败 */
class CreateFailed: public Mods_Exp {
public:
explicit CreateFailed(const std::string &name): Mods_Exp(name) {
;
}
};
/* 提升权限失败 */
class GrandPrivilegesFailed: public Mods_Exp {
public:
explicit GrandPrivilegesFailed(const std::string &name): Mods_Exp(name) {
;
}
};
/* 删除失败 */
class DeleteFailed: public Mods_Exp {
public:
explicit DeleteFailed(const std::string &name): Mods_Exp(name) {
;
}
};
private:
/* 子系统的模块目录 */
std::string home;
/* 创建模块目录*/
void crt_mod_dir(std::string name) {
std::string fullpath = get_mod_path(name);
if (is_exist(fullpath)) {
throw AlreadyExist(name);
} else {
std::filesystem::create_directory(fullpath);
}
}
/* 初始化模块目录,创建启动脚本 */
void init_mod_dir(std::string name) {
std::string fullpath = get_mod_init_path(name);
if (is_exist(fullpath)) {
throw AlreadyExist(name);
}
else {
std::ofstream outFile = std::ofstream(fullpath);
if (outFile) {
outFile << "#!/bin/bash\n";
outFile << "cd \"$(dirname \"$0\")\" || exit\n\n";
outFile << "exec echo hello!";
} else {
throw CreateFailed(name);
}
}
}
/* 初始化一个模块的手册(README.md)*/
void init_mod_man(std::string name) {
std::string fullpath = get_mod_man_path(name);
if(is_exist(fullpath)) {
throw AlreadyExist(fullpath);
}
else {
std::ofstream outFile = std::ofstream(fullpath);
if (outFile) {
outFile << "# ";
outFile << name;
outFile << "\n";
} else {
throw CreateFailed(name);
}
}
}
/* 获取模块的路径 */
std::string get_mod_path(std::string &name) {
return this->home + get_sep() + name;
}
/* 获取模块的启动脚本路径 */
std::string get_mod_init_path(std::string &name) {
return get_mod_path(name) + get_sep()+ "_run.sh";
}
/* 获取模块的手册路径 */
std::string get_mod_man_path(std::string &name) {
return get_mod_path(name) + get_sep()+ "README.md";
}
/* 赋予一个文件755权限 */
void grd_priv(std::string &name) {
std::string fullpath = get_mod_init_path(name);
if (chmod(fullpath.c_str(), 0755) == -1) {
throw GrandPrivilegesFailed("path");
}
}
/* 获取当前可执行程序所在目录 */
static std::string get_curr() {
return std::filesystem::canonical("/proc/self/exe").parent_path().string();
}
/* 文件是否存在 */
static bool is_exist(std::string &path) {
return std::filesystem::exists(path);
}
/* 获取文件系统分隔符号 */
static char get_sep() {
return std::filesystem::path::preferred_separator;
}
/* 删除一整个文件夹 */
static void deleteDirectory(const std::filesystem::path& path) {
try { /* 递归 */
std::filesystem::remove_all(path) > 0;
}
catch (const std::filesystem::filesystem_error& e) {
throw DeleteFailed(e.what());
}
}
};
}
+79
View File
@@ -0,0 +1,79 @@
#include <cstdlib>
#include "lci/utf8.h"
#include "lci/ansi.h"
#include "lci/Drawable.h"
#include "lci/Area.h"
namespace qing {
/* FIXME: 区域类持有可绘制类的引用 */
Area::Area(Drawable *d, int w, int h, int x, int y, int rotate = 0, int fontsize=18)
:Drawable(w, h, x, y, rotate, fontsize) {
this->d = d;
}
/* 获取持有可绘制对象的颜色 */
Color Area::_get(int x, int y) {
return d->get_color(this->x + x, this->y + y);
}
/* 绘制到持有可绘制对象 */
void Area::_p(int x, int y, Color &clr) {
d->point(this->x + x, this->y + y, clr);
}
/* 持有可绘制对象区域移动 */
// void Area::_up(int x, int y, int w, int h, int level) {
// d->up(this->x + x, this->y + y, w, h, level);
// }
//
// void Area::_down(int x, int y, int w, int h, int level) {
// d->down(this->x + x, this->y + y, w, h, level);
// }
//
// void Area::_left(int x, int y, int w, int h, int level) {
// d->left(this->x + x, this->y + y, w, h, level);
// }
//
// void Area::_right(int x, int y, int w, int h, int level) {
// d->right(this->x + x, this->y + y, w, h, level);
// }
/* 将另一个可绘制对象刷新到这里 */
void Area::flush(Drawable *n, bool border) {
Rectangle size = n->get_size();
Rectangle pos = n->get_pos();
Color &clr = n->getFontsColor();
if (border) {
rectangle(pos.w-1, pos.h-1, size.w+1, size.h+1, clr);
}
/* FIXME: Area无需复制?如果是两块区域还是需要的 */
Area *p = dynamic_cast<Area*>(n);
if (p != nullptr) return;
// too slow
// 太慢了
for (int x = 0; x < size.w; ++x) {
for (int y = 0; y < size.h; ++y) {
Color color = n->get_color(x, y);
point(pos.w + x, pos.h + y, color);
}
}
}
/* FIXME: 边界检查应该加在父类方法中 不超出边界 */
/* FIXME: 将border写入成员中,如果有border连同它一起移动 */
/* FIXME: rotate=1时不能正常移动 */
void Area::move(int x, int y) {
// if (x >= 0)
// d->right(this->x, this->y, this->w+x, this->h, x);
// else if (x < 0)
// d->left(this->x+x, this->y, this->w-x, this->h, x);
// if (y >= 0)
// d->down(this->x+x, this->y, this->w, this->h+y, y);
// else if (y <0)
// d->up(this->x+x, this->y+y, this->w, this->h-y, y);
this->x += x;
this->y += y;
}
}
+219
View File
@@ -0,0 +1,219 @@
/*
* FIXME: 现在输入框无法触底
*/
#include <cstring>
#include <vector>
#include "lci/CmdArea.h"
#include "lci/utf8.h"
#include "lci/ansi.h"
constexpr static auto H = 2;
namespace qing {
/* FIXME(20260113): 将输入框做成大部分时候和输入行在同一行 */
CmdArea::CmdArea(Drawable *d, int w, int h, int x, int y, int rotate, int fontsize, bool hasBox)
: Area(d, w, h, x, y, rotate, fontsize), hasBox(hasBox) {
//auto w_std = w - 2 * x;
auto w_std = 5 * fontsize;
auto w_tar = 5 * fontsize;
if (hasBox) {
area = std::make_unique<InputBox>(this, (w_tar < w_std) ? w_tar : w_std, H*fontsize, x, fontsize*2, rotate, fontsize);
}
}
void CmdArea::Input(char c) {
std::unique_lock<std::mutex> lck(this->lock);
this->input += c;
}
void CmdArea::ClearInput() {
std::unique_lock<std::mutex> lck(this->lock);
this->input = "";
}
void CmdArea::update_input() {
std::unique_lock<std::mutex> lck(this->lock);
if (hasBox)
area->print((char*)this->input.c_str(), 0);
}
void CmdArea::clearBox() {
std::unique_lock<std::mutex> lck(this->lock);
auto clr = black;
if (hasBox){
auto size = area->get_size();
area->rectangle_fill(0, 0, size.w-1, size.h, clr);
}
}
void CmdArea::delete_input() {
std::unique_lock<std::mutex> lck(this->lock);
auto l = this->input.length();
this->input = this->input.substr(0, l - 1);
}
std::string CmdArea::get_input_and_clear() {
std::unique_lock<std::mutex> lck(this->lock);
auto cur = this->input;
this->input = "";
return cur;
}
void CmdArea::print(char *utf8, int d) {
auto color = black;
auto fontsize = FontSize_get();
auto half_f = fontsize / 2;
this->lock.lock();
auto subpos =(hasBox) ? area->get_pos() : Rectangle{0, 0};
auto subsize = (hasBox) ? area->get_size() : Rectangle{0, 0};
if (hasBox)
rectangle_fill(subpos.w-1, subpos.h-1, subsize.w+2, subsize.h+2, color);
char *ansi = utf8::to_ansi(utf8);
char *p = ansi;
Rectangle l = get_size();
while (1) {
//int size = ansi::size_gb18030((unsigned char*)p, 4);
/**
* 这个老的矢量字库应该是 GB2312 的
*/
int size = ansi::size_gb2312(*(unsigned char *)p);
if (*p == '\n') {
x_f = 0;
y_f += fontsize;
} else if (*p == '\0') {
break;
} else if (*p == '\t') {
int n = x_f / half_f;
int m = 8 - n % 8;
x_f += m * half_f;
} else if (*p == '\r') {
;
} else if (*p == '\033') {
p++;
if (*p != '[' && *p != ']') continue;
if (*p == ']') {
p++;
while (*p != '\007') {
p++;
}
}
else if (*p == '[') {
p++;
int top = 0, t2 = 0;
char buffer[32], buf2[16];
while((!(*p > 'a' && *p < 'z') && !(*p >'A' && *p < 'Z')) && (*p != '\0')) {
buffer[top++] = *(p++);
}
if (*p == '\0') break;
buffer[top++] = *p;
buffer[top] = '\0';
//printf("%s\n", buffer);
int i = 0;
while (i <= top) {
if (buffer[i] == ';') {
buf2[t2] = '\0';
handle_ansi_code(buf2);
buf2[0] = '\0';
t2 = 0;
} else {
buf2[t2++] = buffer[i];
}
i++;
}
buf2[t2] = '\0';
handle_ansi_code(buf2);
}
} else { // 正常的字
char c;
if (size == 1) {
c = *(p + 1);
*(p + 1) = '\0';
}
drawvfont(x_f, y_f, *(unsigned short*)p, clr);
if (size == 1) {
*(p + 1) = c;
}
x_f += (size == 1) ? fontsize / 2 : fontsize;
}
if (x_f >= l.w - fontsize) {
y_f += fontsize;
x_f = 0;
}
if (y_f >= l.h - (d + 3) * fontsize) { /* FIXME: d加上输入框高度 */
int level = (y_f % fontsize == 0) ? fontsize : y_f % fontsize;
y_f -= level;
up(0, 0, l.w, l.h - d * fontsize, level);
}
p += size;
}
free(ansi);
if (hasBox) {
subpos.w = ((x_f + fontsize + subsize.w) > l.w - fontsize) ? l.w - fontsize - subsize.w : x_f + fontsize;
subpos.h = y_f + fontsize;
area->set_pos(subpos);
this->lock.unlock();
this->update_input();
flush(area.get(), true);
}
}
void CmdArea::handle_ansi_code(char *buffer) {
static std::vector<std::string> v;
std::string cur = buffer;
char c = '\0';
v.push_back(cur);
if (!cur.empty() && std::isalpha((c = cur.back()))) {
for ( const auto& elem: v ) {
if (elem == "32m") {
Color clr = green;
set_color(clr);
} else if (elem == "34m") {
Color clr = blue;
set_color(clr);
} else if (elem == "0m" || elem == "00m") {
Color clr = white;
set_color(clr);
} else if (elem == "1m" || elem == "01m") {
if (v[0] == "40") {
;
}
if (v[1] == "33") {
//Color clr = yellow;
//set_color(clr);
}
}
std::cout << elem << " ";
}
v.clear();
std::cout << std::endl;
}
}
}
+8
View File
@@ -0,0 +1,8 @@
#include "lci/Color.h"
namespace qing {
const Color black=Color(0,0,0);
const Color white=Color(255,255,255);
const Color green=Color(0,255,0);
const Color blue =Color(0,0,255);
const Color red =Color(255,0,0);
}
+102
View File
@@ -0,0 +1,102 @@
#include <unistd.h>
#include "lci/CommonThread.h"
namespace qing {
namespace lci{
void CommonThread::thread_main() {
while (chk() != SSHUT) {
/*进入回字形循环,如果不是进入关闭态
内层循环会一直进行。*/
if (chk() == SSTOP){
/*内层循环,静止状态*/
StopEvent();
}
if (chk() == SSTART) {
/*由静止状态退出时,如果进入开始状态
则进入单次的设置操作*/
WakeEvent();
}
if (chk() == SRUNNING) {
/*由设置阶段退出时,如果是运行状态,则发出提示*/
}
while (chk() == SRUNNING) {
//进入运行态
LoopEvent();
}
/*循环末尾,进行一次清理
由于之前可能是由静止态直接切换到关闭工作
在清理时需要检查成员变量是否是被设置过的*/
ClearEvent();
}
}//入口
CommonThread::CommonThread(std::string name) {
/*构造函数,完成数据域的设置。*/
this->label = name;
//创建线程的函数
this->thread = new std::thread(&CommonThread::thread_main, this);
}
//清除线程
//使线程自主退出的函数必须放到外面或者子类叶子上,
//否则会导致关闭时子类已经被释放,进而导致调用不了
CommonThread::~CommonThread(){
if (this->thread){
delete this->thread;//删除线程类
}
} //销毁线程
//-----------------------------------------------------------
//访问器函数
void CommonThread::SetLabel(std::string name) {
//对线程名字的设置
this->label = name;
}
std::string& CommonThread::GetLabel() {
//对线程名字的获取
return this->label;
}
//-----------------------------------------------------------
//等待线程启动完毕
//线程启动失败返回失败。线程启动成功或者现程结束返回成功。
bool CommonThread::WaitStart(unsigned long n) {
for(unsigned long i=0; i<n; i++){
enum Stat stat = this->chk();
if (stat == SSTOP) return false;
else if (stat == SRUNNING || stat == SSHUT) return true;
else if (stat == SSTART) usleep(1);
}//按次循环
return false;
}//等线程开始
//等线程关闭
void CommonThread::WaitClose() {
thread->join();
}
//-----------------------------------------------------------
void CommonThread::StopEvent(){
this->suspend();
}
void CommonThread::WakeEvent(){
this->run();
}
void CommonThread::LoopEvent(){
usleep(10000);
}
void CommonThread::ClearEvent() {
}
}//lci
}
+359
View File
@@ -0,0 +1,359 @@
#include <cmath>
#include "lci/Drawable.h"
namespace qing {
/* FIXME: 字体宽高分开传入 */
Drawable::Drawable(int w, int h, int x, int y, int rotate, int fontsize) /* 构造可绘制对象 */
: w(w), h(h), x(x), y(y), rotate(rotate), font_w(fontsize), font_h(fontsize) {
zn = zzhinumber; /* 初始化字库 */
vennum = ttunumber;
}
Rectangle Drawable::get_size() { /* 获取表示尺寸的矩形 */
if (rotate == 0 || rotate == 2) {
return Rectangle(w, h);
} else if (rotate == 1 || rotate == 3) {
return Rectangle(h, w);
}
throw Drawable::InvalidRotateMode();
}
Rectangle Drawable::get_pos() { /* 获取表示位置的矩形 */
return Rectangle(x, y);
}
void Drawable::set_pos(Rectangle rect) { /* 设置表示位置的矩形 */
x = rect.w;
y= rect.h;
}
Color Drawable::get_color(int x, int y) { /* 获取颜色 */
if (rotate == 0)
{
return _get(x, y);
}
else if (rotate == 1)
{
return _get(y, h - x - 1);
}
else if (rotate == 2)
{
return _get(w - x - 1, h - y - 1);
}
else if (rotate == 3)
{
return _get(w - y - 1 , x);
}
throw Drawable::InvalidRotateMode();
}
/* 设置颜色 */
void Drawable::set_color(Color &color) {
this->clr = color;
}
/* 设置字体大小 */
void Drawable::FontSize(int fontsize) {
font_w = fontsize;
font_h = fontsize;
}
/* 获取字体大小 */
int Drawable::FontSize_get() {
return font_w;
}
/* 获取字体颜色 */
Color &Drawable::getFontsColor() {
return this->clr;
}
void Drawable::point(int x, int y, Color &clr) { /* 绘制点 */
if (rotate == 0)
{
_p(x, y, clr);
}
else if (rotate == 1)
{
_p(y, h - x - 1, clr);
}
else if (rotate == 2)
{
_p(w - x - 1, h - y - 1, clr);
}
else if (rotate == 3) {
_p(w - y - 1 , x, clr);
}
}
void Drawable::line(int x1, int y1, int x2, int y2, Color &clr) /* 绘制线 */
{
double t, dt;
double i, j;
i = x1;
j = y1;
if (std::fabs(x2 - x1) > std::fabs(y2 - y1))
dt = 1 / (std::fabs(x2 - x1) * 2.0);
else
dt = 1 / (std::fabs(y2 - y1) * 2.0);
for (t = 0; t <= 1.0; t += dt) {
point((int)(i+0.5), (int)(j+0.5), clr);
i += dt * (x2 - x1);
j += dt * (y2 - y1);
}
}
void Drawable::rectangle(int x, int y, int w, int h, Color &clr) /* 绘制矩形 */
{
line(x, y, x+w, y, clr);
line(x+w, y, x+w, y+h, clr);
line(x+w, y+h, x, y+h, clr);
line(x, y+h, x, y, clr);
}
void Drawable::rectangle_fill(int x, int y, int w, int h, Color &clr) /* 填充矩形 */
{
for (int i = 0; i < h; ++i)
{
line(x, y+i, x+w, y+i, clr);
}
}
// void Drawable::up(int x, int y, int w, int h, int level) /* 画面上移 */
// {
// switch (rotate)
// {
// case 0: {
// _up(x, y, w, h, level);
// break;
// }
// case 1: {
// _left(y, x, h, w, level);
// break;
// }
// case 2: {
// _down(this->w - x - w, this->h - y - h, w, h, level);
// break;
// }
// case 3: {
// _right(this->w - y - h, x, h, w, level);
// break;
// }
// }
// }
//
// void Drawable::down(int x, int y, int w, int h, int level) /* 画面下移 */
// {
// switch (rotate)
// {
// case 0: {
// _down(x, y, w, h, level);
// break;
// }
// case 1: {
// _right(y, x, h, w, level);
// break;
// }
// case 2: {
// _up(this->w - x - w, this->h - y - h, w, h, level);
// break;
// }
// case 3: {
// _left(this->w - y - h, x, h, w, level);
// break;
// }
// }
// }
//
// void Drawable::left(int x, int y, int w, int h, int level) /* 画面左移 */
// {
// switch (rotate)
// {
// case 0: {
// _left(x, y, w, h, level);
// break;
// }
// case 1: {
// _down(y, x, h, w, level);
// break;
// }
// case 2: {
// _right(this->w - x - w, this-> h - y - h, w, h, level);
// break;
// }
// case 3: {
// _up(this->w - y - h, x, h, w, level);
// break;
// }
// }
// }
//
// void Drawable::right(int x, int y, int w, int h, int level) /* 画面右移 */
// {
// switch (rotate)
// {
// case 0: {
// _right(x, y, w, h, level);
// break;
// }
// case 1: {
// _up(y, x, h, w, level);
// break;
// }
// case 2: {
// _left(this->w - x - w, this->h - y - h, w, h, level);
// break;
// }
// case 3: {
// _down(this->w - y - h, x, h, w, level);
// break;
// }
// }
// }
/* FIXME: 单独放在一个文件里 */
void Drawable::test() { /* 绘制测试图案 */
Rectangle size = get_size();
Color base;
int k1 = -1;
for (int y=0; y < size.h; ++y) {
Color cur = base;
int k2 = -1;
for (int x=0; x < size.w; x++) {
if (cur.r == 0) k2 = 1;
else if (cur.r == 255) k2 = -1;
cur.r += k2;
cur.g += k2;
cur.b += k2;
point(x, y, cur);
}
if (base.r == 0) k1 = 1;
else if (base.r == 255) k1 = -1;
base.r += k1;
base.g += k1;
base.b += k1;
}
}
/* 移动测试 */
// void Drawable::test1()
// {
// Rectangle size = get_size();
// up(0, 0, size.w, size.h, 10);
// down(0, 0, size.w, size.h, 5);
// left(0, 0, size.w, size.h, 10);
// right(0, 0, size.w, size.h, 5);
// }
unsigned char *Drawable::drawvect(int x, int y, unsigned char *vect, int width, int high, Color &clr)
// x, y是左上角坐标,vect指向矢量图数据,width和high分别代表要绘制的图的宽和高(好像宽需要=高)
{
if (!vect) return nullptr;
int i = 0, j = 1; // 指向第一对数字
unsigned char ix, iy; // 从数组取出的第一对坐标放在这
double x1, y1;//, x2, y2; // 绘制直线的实数的坐标
if (high == 0) high = width; // 调节高宽
int ii1, jj1, ii2, jj2; // 最后算出的绘制直线的坐标
int isend = 0; //是否到达曲线尾
int isallend = 0; // 是否结束整个图像
do {
ix = vect[i];
iy = vect[j];
isend = ix & 1; // 奇数代表曲线尾
isallend = iy & 1; // 奇数代表整个图像结束
if (isallend) {
i += 2;
break; // 整个图像结束返回
}
if (isend) { // 如果这里曲线结束什么也不画进到下一条曲线
i += 2;
j += 2;
continue;
}
ix /= 2;
iy /= 2;
x1 = ix;
y1 = iy;
x1 = width * x1 / 128.0;
y1 = high * y1 / 128.0;
ii1 = (int)(x1 + x);
jj1 = (int)(y1 + y);
int k;
for (k = 0; k < 100000; k++) {
//x2 = x1;
//y2 = y1;
ii2 = ii1;
jj2 = jj1;
i += 2;
j += 2;
ix = vect[i];
iy = vect[j];
isend = ix & 1; //奇数代表曲线尾
isallend = iy & 1; //奇数代表整个图像结束
ix /= 2;
iy /= 2;
x1 = ix;
y1 = iy;
x1 = width * x1 / 128.0;
y1 = high * y1 / 128.0;
ii1 = (int)(x1 + x);
jj1 = (int)(y1 + y);
line(ii1, jj1, ii2, jj2, clr);
if (isend || isallend)
break;
}
i += 2; //指向下一条曲线
j += 2;
} while (!isallend);
return vect + i;
}
void Drawable::drawvfont(int x, int y, unsigned short z, Color &clr) {
int i, j, k, ii;
j = -1;
for (i = 0; i < zn; i++) {
if (chss[i] == z) {
j = i;
break;
}
}
if (j < 0) return;
k = 0;
for (i = 0; i < vennum; i++) {
if (k == j)
break;
for (ii =i; ii <vennum; ii++) {
if (vec[2*ii + 1] & 1) {
k++;
i = ii;
break;
}
}
}
if (k < j) return;
drawvect(x, y, vec + 2 * i, font_w, font_h, clr);
}
}
+289
View File
@@ -0,0 +1,289 @@
#include <iostream>
#include <cstring>
#include <cstdint>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include "lci/FrameBuf.h"
#include "lci/Area.h"
namespace qing {
/* 这个构造函数太长,应该还可以再提炼一下 */
FrameBuf::FrameBuf(const char *path, int rotate, int fontsize)
: Drawable(0, 0, 0, 0, rotate, fontsize) {
//int open_dev(const char *path)
{
if (d != -1) {
throw std::runtime_error("Device already opening.");
}
if ((d = open(path, O_RDWR)) == -1) {
throw std::runtime_error("Error openning framebuffer device");
}
}
//void init_info()
{
if (ioctl(d, FBIOGET_VSCREENINFO, &var_info) < 0) {
close(d);
d=-1;
throw std::runtime_error("Error reading screen variable information");
}
if (ioctl(d, FBIOGET_FSCREENINFO, &fix_info) < 0) {
close(d);
d=-1;
throw std::runtime_error("Error reading fixed information");
}
}
std::cout << "res:\t\t" << var_info.xres << "," << var_info.yres << std::endl;
std::cout << "res_virtual:\t" << var_info.xres_virtual << "," << var_info.yres_virtual << std::endl;
std::cout << "bpp:\t\t" << var_info.bits_per_pixel << std::endl;
w = var_info.xres_virtual;
h = var_info.yres_virtual;
/*
* 不知道用不用虚拟尺寸,目前是用,在虚拟机上的效果不清楚。我在虚拟机上见过虚拟比实际小的。
*
* 而我的机器人上则是虚拟尺寸比物理尺寸大。
*/
//long v_size = (long)var_info.xres * var_info.yres * var_info.bits_per_pixel / 8;
long v_size = (long)var_info.xres_virtual * var_info.yres_virtual * var_info.bits_per_pixel / 8;
long logic_size = (long)fix_info.smem_len;
int delta = logic_size - v_size;
// 如果实长大于虚长,那么就证明这个缓冲区存在过量
double delta_x = (double)delta / var_info.xres;
double delta_y = (double)delta / var_info.yres;
double delta_bpp = (double)delta / var_info.bits_per_pixel;
std::cout << "virtual size:\t" << v_size << std::endl;
std::cout << "logic size:\t" << logic_size<< std::endl;
std::cout << "delta: \t" << delta<< std::endl;
std::cout << "delta x: \t" << delta_x<< std::endl;
std::cout << "delta y: \t" << delta_y<< std::endl;
std::cout << "delta bpp: \t" << delta_bpp<< std::endl;
if (delta == 0) {
// 如果实长和虚长是相等的,那么就证明这个缓冲区无过量
x_over = 0;
}
else if (delta > 0) {
// 是否存在小数差距
if (delta_y - (int)delta_y != 0) {
//close(d);
//d=-1;
//throw std::runtime_error("y_over");
x_over = delta_y * 8 / var_info.bits_per_pixel;
}
else {
// 计算横轴过量
x_over = delta_y * 8 / var_info.bits_per_pixel;
}
}
// 计算行跳量
line_jump = (var_info.xres_virtual + x_over) * var_info.bits_per_pixel / 8;
//
buf = (char*)mmap(buf, fix_info.smem_len, PROT_WRITE, MAP_SHARED, d, fix_info.smem_start);
if (buf == MAP_FAILED) {
close(d);
d=-1;
throw std::runtime_error("Failed mapping of the frame buffer to memory");
}
clear();
}
FrameBuf::~FrameBuf() { /* 析构函数 */
clear();
munmap(buf, fix_info.smem_len);
close(d);
}
/* 获取像素比特 */
int FrameBuf::get_bpp() { return var_info.bits_per_pixel; }
/* 32位像素比特的绘制点 */
void FrameBuf::p32(__u32 ux, __u32 uy, Color &color)
{
if (ux < 0 || ux >= var_info.xres_virtual
|| uy < 0 || uy >= var_info.yres_virtual)
return;
char *p = buf + (uy * line_jump + ux * 4);
*p++ = color.b;
*p++ = color.g;
*p++ = color.r;
}
/* 16位像素比特的绘制点 */
void FrameBuf::p16(__u32 ux, __u32 uy, Color &color) {
if (ux < 0 || ux >= var_info.xres_virtual
|| uy < 0 || uy >= var_info.yres_virtual)
return;
char *p = buf + (uy * line_jump + ux * 2);
uint16_t pixel_value = 0;
pixel_value |= (color.r >> 3) << 11; //5
pixel_value |= (color.g >> 2) << 5; //6
pixel_value |= (color.b >> 3); //5
*reinterpret_cast<uint16_t*>(p) = pixel_value;
}
/* 获取16像素比特时的像素颜色 */
Color FrameBuf::get16(__u32 ux, __u32 uy) {
char *p = buf + uy * line_jump + ux * get_bpp() / 8;
unsigned char r = (*(short*)p >> 11) & 0x1F;
unsigned char g = (*(short*)p >> 5) & 0x3F;
unsigned char b = (*(short*)p) & 0x1F;
return Color((r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2));
}
/* 获取32位像素比特时的像素颜色 */
Color FrameBuf::get32(__u32 ux, __u32 uy) {
char *p = buf + uy * line_jump + ux * get_bpp() / 8;
unsigned char r = *(p++);
unsigned char g = *(p++);
unsigned char b = *(p++);
return Color(r, g, b);
}
/* 覆盖获取点方法 */
Color FrameBuf::_get(int x, int y) {
switch (get_bpp()) {
case 16:
return get16((__u32)x, (__u32)y);
case 32:
return get32((__u32)x, (__u32)y);
}
throw FrameBuf::UnknowBPP();
}
/* 覆盖绘制点方法 */
void FrameBuf::_p(int x, int y, Color &clr) {
int bpp = get_bpp();
if (bpp == 16) {
p16((__u32)x, (__u32)y, clr);
} else if (bpp == 32) {
p32((__u32)x, (__u32)y, clr);
}
}
/* 覆盖上移方法 */
// void FrameBuf::_up(int x, int y, int w, int h, int level){
// int l = get_bpp() / 8;
// int oneline = w * l;
// char *s = buf + y * line_jump + x * l;
// char *d = s + level * line_jump;
// for (int _ = 0; _ < h - level; ++_) {
// memcpy(s, d, oneline);
// s += line_jump;
// d += line_jump;
// }
// for (int _ = 0; _ < level; ++_) {
// memset(s, 0, oneline);
// s += line_jump;
// }
// }
//
// /* 覆盖下移方法 */
// void FrameBuf::_down(int x, int y, int w, int h, int level) {
// int l = get_bpp() / 8;
// int oneline = w * l;
// long d = level * line_jump;
// char *start = buf + y * line_jump + x * l;
// char *src = start + h * line_jump;
// char *dist = src - d;
//
// do {
//
// src -= line_jump;
// dist -= line_jump;
// memcpy(src, dist, oneline);
//
// } while(dist != start);
//
// do {
//
// src -= line_jump;
// memset(src, 0, oneline);
//
// } while (src != start);
//
// }
//
// /* 覆盖左移方法 */
// void FrameBuf::_left(int x, int y, int w, int h, int level) {
//
// int l = get_bpp() / 8;
// int d = level * l;
// int length = (w - level) * l;
// char *start = buf + y * line_jump + x * l;
// char *src = start + h * line_jump;
// do {
// src -= line_jump;
// memcpy(src, src + d, length);
// memset(src + length, 0, d);
// } while (src != start);
//
// }
//
// /* 覆盖右移方法 */
// /*!*/
// void FrameBuf::_right(int x, int y, int w, int h, int level) {
// int l = get_bpp() / 8;
// int d = level * l;
// int length = (w - level) * l;
// char *start = buf + y * line_jump + x * l;
// char *p = start + h * line_jump;
// char *tmp = new char[length];
// do {
// p -= line_jump;
// char *src = p + d;
// memcpy(tmp, p, length);
// memcpy(src, tmp, length);
// memset(p, 0, d);
// } while (p != start);
// delete[] tmp;
// }
/* 清理方法 */
void FrameBuf::clear() {
memset(buf, 0, fix_info.smem_len);
}
/* 将一个别的可绘制区域刷新到这个上面,也许可以绘制标头 */
void FrameBuf::flush(Drawable *n, bool border) {
Color &clr = n->getFontsColor();
Rectangle size = n->get_size();
Rectangle pos = n->get_pos();
if (border) {
rectangle(pos.w-1, pos.h-1, size.w+2-1, size.h+2-1, clr);
rectangle(pos.w-2, pos.h-2, size.w+4-1, size.h+4-1, clr);
rectangle_fill(pos.w-2, pos.h-_HEAD_LEN-2+1, size.w+4-1, _HEAD_LEN, clr);
}
Area *p = dynamic_cast<Area*>(n);
if (p != nullptr) return;
// too slow 太慢
for (int x = 0; x < size.w; ++x) {
for (int y = 0; y < size.h; ++y) {
Color color = n->get_color(x, y);
point(pos.w + x, pos.h + y, color);
}
}
}
}
+57
View File
@@ -0,0 +1,57 @@
#include "lci/utf8.h"
#include "lci/ansi.h"
#include "lci/InputBox.h"
namespace qing {
InputBox::InputBox(Drawable *d, int w, int h, int x, int y, int rotate = 0, int fontsize=18): Area(d, w, h, x, y, rotate, fontsize) {
this->d = d;
}
void InputBox::print(char *utf8, int _line) {
char *ansi = utf8::to_ansi(utf8);
char *p = ansi;
int fontsize = FontSize_get();
int x_f = 0, y_f = _line * fontsize;
Rectangle l = get_size();
while (1) {
//int size = ansi::size_gb18030((unsigned char*)p, 4);
/* 这个汉字库应该是 GB2312 的 */
int size = ansi::size_gb2312(*(unsigned char *)p);
if (*p == '\0') {
break;
} else { // 正常的字
char c;
if (size == 1) {
c = *(p + 1);
*(p + 1) = '\0';
}
drawvfont(x_f, y_f, *(unsigned short*)p, clr);
if (size == 1) {
*(p + 1) = c;
}
x_f += (size == 1) ? fontsize / 2 : fontsize;
}
if (x_f >= l.w - fontsize) {
break;
}
p += size;
}
free(ansi);
int y2 = y_f + fontsize-2;
line(x_f + 2, y2, x_f + fontsize - 4, y2, clr);
}
}
+99
View File
@@ -0,0 +1,99 @@
#include <iostream>
#include <cstring>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include "lci/KeyboardEvent.h"
namespace qing {
/* FIXME: 划分代码块 */
KeyboardEvent::KeyboardEvent() {
const char *path = "/dev/input";
struct dirent *dir;
DIR *p = opendir(path);
if(p == NULL) throw std::runtime_error("opendir");
while ((dir = readdir(p)) != NULL) {
if (!is_event_with_number(dir->d_name)) continue;
char full_name[257] = {'\0'};
snprintf(full_name, sizeof(full_name), "%s/%s", path, dir->d_name);
struct stat buf;
if (stat(full_name, &buf) == -1) {
throw std::runtime_error("stat");
}
if(S_ISDIR(buf.st_mode)) continue;
else if (pool.find(std::string(full_name)) == pool.end()){
int d = open(full_name, O_RDONLY);
if (d == -1) {
;
} else {
set_noblocking(d);
pool.insert(std::make_pair(std::string(full_name), d));
}
}
}
closedir(p);
}
KeyboardEvent::~KeyboardEvent() {
std::map<std::string, int>::iterator it;
for (it = pool.begin(); it != pool.end(); ++it) {
close(it->second);
}
}
/* FIXME: 传入堵塞时间 */
struct input_event KeyboardEvent::get() {
fd_set set;
FD_ZERO(&set);
for (auto it = pool.begin(); it != pool.end(); ++it) {
FD_SET(it->second, &set);
}
struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 60000;
if (select(this->maxd()+1, &set, NULL, NULL, &timeout) == -1) {
throw std::runtime_error("select");
}
for (auto it = pool.begin(); it != pool.end(); ++it) {
if (it->second != -1 && FD_ISSET(it->second, &set)) {
struct input_event ev;
if (read(it->second, &ev, sizeof(ev)) == sizeof(ev)) {
return ev;
}
}
}
throw NoEvent("");
}
bool KeyboardEvent::is_event_with_number(const char *filename) {
size_t len = strlen(filename);
if (len <= 5) return false;
if (strncmp(filename, "event", 5) != 0)
return false;
for (size_t i=5; i<len; ++i)
if (!isdigit(filename[i]))
return false;
/* 所有条件都满足 */
return true;
}
void KeyboardEvent::set_noblocking(int d) {
int sig = fcntl(d, F_GETFL, 0);
if (sig == -1) throw std::runtime_error("fcntl");
if (fcntl(d, F_SETFL, sig|O_NONBLOCK) == -1)
throw std::runtime_error("fcntl");
}
int KeyboardEvent::maxd() {
int maxValue = pool.begin()->second;
std::map<std::string, int>::iterator it;
for (it = pool.begin(); it != pool.end(); ++it)
if (it->second > maxValue)
maxValue = it->second;
return maxValue;
}
}
+22
View File
@@ -0,0 +1,22 @@
#include "lci/ansi.h"
namespace qing{
/* 这个函数并不完全
int ansi::size_gb18030(unsigned char *p, size_t maxLen) {
if (*p < 0x80) {
return 1;
} else if ((*p > 0x81 && *p <= 0xFE) &&
((*(p+1) >= 0x40 && *(p+1) <= 0x7E) || (*(p+1) >= 0x80 && *(p+1) <= 0xFE))) {
return 2;
} else if (maxLen >= 4 && (*p == 0x90 || *p == 0x91 || *p == 0xE8 || *p == 0xE9 ||
(*p >= 0x8E && *p <= 0xEF) || (*p >= 0xF8 && *p <= 0xFB)) &&
(*(p+1) & 0xC0) == 0x80 && (*(p+2) & 0xC0) == 0x80 && (*(p+3) & 0xC0) == 0x80) {
return 4; //注意这里是简化判断
} else {
throw ansi::WrongLength();
}
}
*/
int ansi::size_gb2312(unsigned char first_byte) {
return (first_byte >= 0xA1 && first_byte <= 0xFE) ? 2 : 1;
}
}
+45
View File
@@ -0,0 +1,45 @@
#include"lci/fsm.h"
namespace qing{
namespace lci{
Fsm::Fsm(){
this->stat = SSTOP;
}
//------------------------------------------------------
enum Stat Fsm::chk() {
std::unique_lock<std::mutex> lck(this->lock);
return this->stat;
}//check
void Fsm::suspend(){
std::unique_lock<std::mutex> lck(this->lock);
cv.wait(lck);
}//suspend
//-------------------------------------------------------
void Fsm::wake() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SSTART;
cv.notify_one();
}//wake
void Fsm::stop() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SSTOP;
cv.notify_one();
}//stop
void Fsm::shut() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SSHUT;
cv.notify_one();
}//close
void Fsm::run() {
std::unique_lock<std::mutex> lck(this->lock);
this->stat = SRUNNING;
cv.notify_one();
}//run
}//lci
}//qing
+20697
View File
File diff suppressed because it is too large. Load diff
+60
View File
@@ -0,0 +1,60 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iconv.h>
#include "lci/utf8.h"
namespace qing {
int utf8::size(const unsigned char c) {
if ((c >> 7) == 0){
// 0xxxxxxx
return 1;
} else if ((c >> 5) == 0b110) {
// 110xxxxx 10xxxxxx
return 2;
} else if ((c >> 4) == 0b1110) {
// 1110xxxx 10xxxxxx 10xxxxxx
return 3;
} else if ((c >> 3) == 0b11110) {
// 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
return 4;
} else {
return 0;
}
}
char *utf8::to_ansi(char *utf8String) {
//@20231120:楠舍科技刘仁
//@20240528,qing:输出字符串的长度不确定
iconv_t ic;
ic = iconv_open("GB18030", "UTF-8");
if (ic == (iconv_t)-1){
perror("iconv_open");
return NULL;
}
//设置输出字符串的大小为2倍输入
size_t InputLength = strlen(utf8String);
size_t OutputLength = InputLength * 2;
char *ansiString = (char*)malloc(OutputLength);
if (ansiString == NULL) {
perror("malloc");
iconv_close(ic);
return NULL;
}
char *InputBuffer = (char*)utf8String;
char *OutputBuffer = ansiString;
size_t result = iconv(ic, &InputBuffer, &InputLength, &OutputBuffer, &OutputLength);
if (result == (size_t)-1) {
perror("iconv");
free(ansiString);
iconv_close(ic);
return NULL;
}
iconv_close(ic);
*OutputBuffer = '\0';
return ansiString;
}
}
+85
View File
@@ -0,0 +1,85 @@
#include "nn/ILayer.hpp"
#include <stdexcept>
/* 获取函数对象 - 获取激活函数 */
f_t ILayer::getf(ActivationFunc type) {
switch (type) {
case ActivationFunc::ReLU:
return relu;
case ActivationFunc::Leaky_ReLU:
return leaky_relu;
case ActivationFunc::Sigmoid:
return sigmoid;
case ActivationFunc::Tanh:
return tanh;
default:
throw std::invalid_argument("ActivationFunc function does not support.");
}
}
/* 获取函数对象 - 求激活函数导数 */
f_t ILayer::getfdot(ActivationFunc type) {
switch (type) {
case ActivationFunc::ReLU:
return relu_derivative;
case ActivationFunc::Leaky_ReLU:
return leaky_relu_derivative;
case ActivationFunc::Sigmoid:
return sigmoid_derivative;
case ActivationFunc::Tanh:
return tanh_derivative;
default:
throw std::invalid_argument("ActivationFunc Derivative does not support.");
}
}
const char* ILayer::get_f_type(ActivationFunc& af) {
switch (af) {
case ActivationFunc::Sigmoid:
return "Sigmoid";
case ActivationFunc::ReLU:
return "ReLU";
case ActivationFunc::Leaky_ReLU:
return "Leaky_ReLU";
}
throw std::runtime_error("What activation function");
}
ILayer::ActivationFunc ILayer::parse_ff_type(const std::string& name) {
if(name == "Sigmoid") {
return ActivationFunc::Sigmoid;
} else if (name == "ReLU") {
return ActivationFunc::ReLU;
} else if (name == "Leaky_ReLU") {
return ActivationFunc::Leaky_ReLU;
} else {
throw std::runtime_error("What activation function");
}
}
/* ReLU 激活函数 */
float ILayer::relu(float x) { return (x > 0.0) ? x : 0.0; }
/* ReLU 导数 */
float ILayer::relu_derivative(float x) { return (x > 0) ? 1: 0; }
/*
* leaky ReLU 激活函数
*
* NOTE: alpha太小或者选择使用ReLU函数的情况下,会导致梯度消失
* NOTE: Leaky ReLU 不会使梯度完全消失,但也会使它变得很小
*/
float ILayer::leaky_relu(float x) { return (x > 0) ? x : LEAKY_RELU_ALPHA * x; }
/* leaky ReLU 导数 */
float ILayer::leaky_relu_derivative(float x) { return (x > 0) ? 1.0f : LEAKY_RELU_ALPHA; }
/* Sigmoid 激活函数 */
float ILayer::sigmoid(float x) { return 1.0 / (1.0 + std::exp(-x)); }
/* Sigmoid 导数 */
float ILayer::sigmoid_derivative(float x)
{
double s = sigmoid(x);
return s * (1.0 - s);
}
/* Tanh 激活函数 */
float ILayer::tanh(float x) { return std::tanh(x); }
/* Tanh 导数 */
float ILayer::tanh_derivative(float x) { return 1.0f - x * x; }
+144
View File
@@ -0,0 +1,144 @@
#include "nn/Matrix.hh"
namespace qing {
template <typename T>
Matrx<T>::Matrx(): row(0), col(0) {}
template <typename T>
Matrx<T>::Matrx(const long row, const long col)
: row(row), col(col) { /* 该构造函数用于从头开始构造 */
arr = std::vector<T>(col * row);
}
template<typename T>
Matrx<T>::Matrx(const long row, const long col, const std::vector<T>& src)
: row(row), col(col) { /* 该构造函数用于通过已有的vector进行构造 */
arr = src;
}
template<typename T>
const T& Matrx<T>::get(long row, long col) const { /* 访问器 - 访问矩阵元素 */
return arr[col * this->row + row];
}
template<typename T>
T& Matrx<T>::witch(long row, long col) { /* 访问器 - 访问矩阵元素 */
return arr[col * this->row + row];
}
template<typename T>
const std::vector<T>& Matrx<T>::get_data() const{ /* 访问器 - 用于获取原始数据 */
return arr;
}
template<typename T>
const long Matrx<T>::get_col() const { /* 获取列数 */
return col;
}
template<typename T>
const long Matrx<T>::get_row() const { /* 获取行数 */
return row;
}
template<typename T>
bool Matrx<T>::has_same_shape(const Matrx<T>& other) { /* 两个矩阵是否具有相同形状 */
return other.get_col() == this->get_col() || other.get_row() == this->get_row();
}
template<typename T>
bool Matrx<T>::has_dot_shape(const Matrx<T>& other) { /* 具有矩阵乘法的形状 */
return this->get_col() == other.get_row();
}
template<typename T>
void Matrx<T>::assert_same_shape(const Matrx<T>& other) { /* 断言两个矩阵具有相同形状 */
if (!has_same_shape(other))
throw std::invalid_argument("The shapes of matrix are not same.");
}
template<typename T>
void Matrx<T>::assert_dot_shape(const Matrx<T>& other) { /* 断言两个矩阵具有点积前置形状 */
if (!has_dot_shape(other)) {
throw std::invalid_argument("The shapes of matrix can not be dot.");
}
}
template<typename T>
Matrx<T> Matrx<T>::operator+(const Matrx<T>& other) { /* 加法操作符重定义 - 矩阵加法 */
assert_same_shape(other); /* 检查两个矩阵形状是否一致 */
std::vector<T> v0 = this->get_data();
std::vector<T> vec = other.get_data();
for (auto it = v0.begin(), it_or = vec.begin(); it != v0.end(); ++it, ++it_or ) {
*it += *it_or;
}
return Matrx<T>{this->get_row(), this->get_col(), v0};
}
template<typename T>
Matrx<T> Matrx<T>::operator+(T& item) { /* 矩阵加法广播 */
auto matrix = *this;
for (auto it = arr.begin(); it != arr.end(); ++it) {
*it += item;
}
return matrix;
}
template<typename T>
Matrx<T> Matrx<T>::operator-(const Matrx<T>& other) { /* 操作符重载 - 矩阵减法 */
assert_same_shape(other); /* 检查两个矩阵形状是否一致 */
std::vector<T> v0 = this->get_data();
std::vector <T> vec = other.get_data();
for (auto it = v0.begin(), it_or = vec.begin(); it != v0.end(); ++it, ++it_or ) {
*it -= *it_or;
}
return Matrx{this->get_row(), this->get_col(), v0};
}
template<typename T>
Matrx<T> Matrx<T>::operator-(T& item) { /* 操作符重载 - 矩阵减法广播 */
auto matrix = *this;
for (auto it = arr.begin(); it != arr.end(); ++it) {
*it -= item;
}
return matrix;
}
template<typename T>
const Matrx<T> Matrx<T>::operator*(const Matrx<T>& other) { /* 操作符重载 - 矩阵乘法 */
assert_dot_shape(other); /* 它们的形状是否能够点积 */
auto matrix = Matrx<T>(this->get_row(), other.get_col());
for (long i=0; i<this->get_row(); ++i) {/* 点积运算 */
for (long j=0; j<other.get_col(); ++j) {
T sum{0};
for (long k=0; k<this->get_col(); ++k) {
sum += this->get(i, k) * other.get(k, j);
}
matrix.witch(i, j) = sum;
}
}
return matrix;
}
template<typename T>
Matrx<T> Matrx<T>::operator*(T& item) { /* 操作符重载 - 矩阵乘法广播 */
std::vector<T> vec = this->get_data();
for (auto it = vec.begin(); it != vec.end(); ++it) {
*it *= item;
}
return Matrx<T>{this->get_row(), this->get_col(), vec};
}
template<typename T>
Matrx<T> Matrx<T>::operator/(T& item) { /* 操作符重载 - 矩阵除法广播 */
std::vector<T> vec = this->get_data();
for (auto it = vec.begin(); it != vec.end(); ++it) {
*it /= item;
}
return Matrx<T>(this->get_row(), this->get_col(), vec);
}
template class Matrx<float>; /* 显式实例化 */
template class Matrx<double>;
}
+396
View File
@@ -0,0 +1,396 @@
/*
* MatrixCuda.cu - MatrixCuda类的CUDA实现文件
*
* 这个文件包含MatrixCuda类的具体实现和CUDA内核的显式实例化
* 注意:这个文件需要使用nvcc编译器编译
*/
#include "nn/MatrixCuda.cuh"
#include "nn/Matrix.hh" // 用于与现有Matrx类交互
namespace qing {
// ============================================================================
// 显式实例化CUDA内核函数
// ============================================================================
namespace cuda_kernels {
// float类型内核显式实例化
template __global__ void matrix_add_kernel<float>(
const float* A, const float* B, float* C,
long rows, long cols, long total_elements);
template __global__ void matrix_sub_kernel<float>(
const float* A, const float* B, float* C,
long rows, long cols, long total_elements);
template __global__ void matrix_scalar_mul_kernel<float>(
const float* A, float scalar, float* C, long total_elements);
template __global__ void matrix_scalar_add_kernel<float>(
const float* A, float scalar, float* C, long total_elements);
template __global__ void matrix_transpose_kernel<float>(
const float* A, float* C, long rows, long cols);
template __global__ void matrix_hadamard_kernel<float>(
const float* A, const float* B, float* C, long total_elements);
template __global__ void matrix_copy_kernel<float>(
const float* src, float* dst, long total_elements);
// double类型内核显式实例化
template __global__ void matrix_add_kernel<double>(
const double* A, const double* B, double* C,
long rows, long cols, long total_elements);
template __global__ void matrix_sub_kernel<double>(
const double* A, const double* B, double* C,
long rows, long cols, long total_elements);
template __global__ void matrix_scalar_mul_kernel<double>(
const double* A, double scalar, double* C, long total_elements);
template __global__ void matrix_scalar_add_kernel<double>(
const double* A, double scalar, double* C, long total_elements);
template __global__ void matrix_transpose_kernel<double>(
const double* A, double* C, long rows, long cols);
template __global__ void matrix_hadamard_kernel<double>(
const double* A, const double* B, double* C, long total_elements);
template __global__ void matrix_copy_kernel<double>(
const double* src, double* dst, long total_elements);
} // namespace cuda_kernels
// ============================================================================
// MatrixCuda类与现有Matrx类的交互实现
// ============================================================================
// 从Matrx<float>转换到MatrixCuda<float>
template <>
CUDA_HOST MatrixCuda<float> MatrixCuda<float>::from_matrx(const Matrx<float>& mat) {
long rows = mat.get_row();
long cols = mat.get_col();
const std::vector<float>& data = mat.get_data();
return MatrixCuda<float>(rows, cols, data);
}
// 从MatrixCuda<float>转换到Matrx<float>
template <>
CUDA_HOST Matrx<float> MatrixCuda<float>::to_matrx() const {
sync_device_to_host();
return Matrx<float>(rows_, cols_, host_data_);
}
// 从Matrx<double>转换到MatrixCuda<double>
template <>
CUDA_HOST MatrixCuda<double> MatrixCuda<double>::from_matrx(const Matrx<double>& mat) {
long rows = mat.get_row();
long cols = mat.get_col();
const std::vector<double>& data = mat.get_data();
return MatrixCuda<double>(rows, cols, data);
}
// 从MatrixCuda<double>转换到Matrx<double>
template <>
CUDA_HOST Matrx<double> MatrixCuda<double>::to_matrx() const {
sync_device_to_host();
return Matrx<double>(rows_, cols_, host_data_);
}
// ============================================================================
// 优化的矩阵乘法实现(使用共享内存)
// ============================================================================
/**
* @brief 使用共享内存优化的矩阵乘法内核
*
* 这个内核使用平铺(tiling)技术来提高矩阵乘法的性能
* 每个线程块计算输出矩阵的一个子块
*/
template <typename T>
__global__ void matrix_mul_optimized_kernel(
const T* A, const T* B, T* C,
long A_rows, long A_cols, long B_cols,
long tile_size) {
// 共享内存声明
extern __shared__ T shared_mem[];
T* tile_A = shared_mem;
T* tile_B = &shared_mem[tile_size * tile_size];
// 线程块索引
long block_row = blockIdx.y;
long block_col = blockIdx.x;
// 线程索引
long thread_row = threadIdx.y;
long thread_col = threadIdx.x;
// 输出矩阵中的位置
long row = block_row * tile_size + thread_row;
long col = block_col * tile_size + thread_col;
T sum = T(0);
// 遍历所有平铺
for (long tile_idx = 0; tile_idx < (A_cols + tile_size - 1) / tile_size; ++tile_idx) {
// 从全局内存加载到共享内存
long A_col = tile_idx * tile_size + thread_col;
long B_row = tile_idx * tile_size + thread_row;
if (row < A_rows && A_col < A_cols) {
tile_A[thread_row * tile_size + thread_col] = A[row * A_cols + A_col];
} else {
tile_A[thread_row * tile_size + thread_col] = T(0);
}
if (B_row < A_cols && col < B_cols) {
tile_B[thread_row * tile_size + thread_col] = B[B_row * B_cols + col];
} else {
tile_B[thread_row * tile_size + thread_col] = T(0);
}
__syncthreads();
// 计算平铺内的点积
for (long k = 0; k < tile_size; ++k) {
sum += tile_A[thread_row * tile_size + k] * tile_B[k * tile_size + thread_col];
}
__syncthreads();
}
// 将结果写回全局内存
if (row < A_rows && col < B_cols) {
C[row * B_cols + col] = sum;
}
}
// ============================================================================
// 优化的矩阵乘法方法
// ============================================================================
/**
* @brief 使用优化CUDA内核的矩阵乘法
*
* 这个方法使用共享内存和平铺技术来加速矩阵乘法
* 对于大矩阵,性能比朴素算法有显著提升
*/
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::matmul_optimized(const MatrixCuda<T>& other) const {
assert_dot_shape(other);
MatrixCuda<T> result(rows_, other.cols_);
// 确保数据在设备上
sync_host_to_device();
other.sync_host_to_device();
result.allocate_device_memory();
// 设置平铺大小(典型值为16或32)
const long tile_size = 16;
// 计算线程块和网格大小
dim3 block_size(tile_size, tile_size);
dim3 grid_size((other.cols_ + tile_size - 1) / tile_size,
(rows_ + tile_size - 1) / tile_size);
// 计算共享内存大小
size_t shared_mem_size = 2 * tile_size * tile_size * sizeof(T);
// 启动优化内核
matrix_mul_optimized_kernel<T><<<grid_size, block_size, shared_mem_size>>>(
device_data_, other.device_data_, result.device_data_,
rows_, cols_, other.cols_, tile_size
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
// 显式实例化优化矩阵乘法
template MatrixCuda<float> MatrixCuda<float>::matmul_optimized(const MatrixCuda<float>& other) const;
template MatrixCuda<double> MatrixCuda<double>::matmul_optimized(const MatrixCuda<double>& other) const;
// ============================================================================
// 使用cuBLAS的矩阵乘法(如果可用)
// ============================================================================
#ifdef HAVE_CUBLAS
#include <cublas_v2.h>
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::matmul_cublas(const MatrixCuda<T>& other) const {
assert_dot_shape(other);
MatrixCuda<T> result(rows_, other.cols_);
// 确保数据在设备上
sync_host_to_device();
other.sync_host_to_device();
result.allocate_device_memory();
// 创建cuBLAS句柄
cublasHandle_t handle;
cublasCreate(&handle);
// 设置cuBLAS参数
T alpha = T(1);
T beta = T(0);
if constexpr (std::is_same_v<T, float>) {
cublasSgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
other.cols_, rows_, cols_,
&alpha,
other.device_data_, other.cols_,
device_data_, cols_,
&beta,
result.device_data_, other.cols_);
} else if constexpr (std::is_same_v<T, double>) {
cublasDgemm(handle, CUBLAS_OP_N, CUBLAS_OP_N,
other.cols_, rows_, cols_,
&alpha,
other.device_data_, other.cols_,
device_data_, cols_,
&beta,
result.device_data_, other.cols_);
}
// 销毁cuBLAS句柄
cublasDestroy(handle);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
// 显式实例化cuBLAS矩阵乘法
template MatrixCuda<float> MatrixCuda<float>::matmul_cublas(const MatrixCuda<float>& other) const;
template MatrixCuda<double> MatrixCuda<double>::matmul_cublas(const MatrixCuda<double>& other) const;
#endif // HAVE_CUBLAS
// ============================================================================
// 测试函数
// ============================================================================
/**
* @brief 测试MatrixCuda类的功能
*
* 这个函数用于验证MatrixCuda类的正确性
* 可以单独编译和运行进行测试
*/
template <typename T>
CUDA_HOST void test_matrix_cuda() {
std::cout << "Testing MatrixCuda<" << typeid(T).name() << ">..." << std::endl;
try {
// 测试1:创建矩阵
std::cout << "Test 1: Creating matrices..." << std::endl;
MatrixCuda<T> A = MatrixCuda<T>::zeros(3, 3);
MatrixCuda<T> B = MatrixCuda<T>::ones(3, 3);
MatrixCuda<T> C = MatrixCuda<T>::identity(3);
A.print("A (zeros)");
B.print("B (ones)");
C.print("C (identity)");
// 测试2:矩阵加法
std::cout << "\nTest 2: Matrix addition..." << std::endl;
MatrixCuda<T> D = A + B;
D.print("D = A + B");
// 测试3:标量运算
std::cout << "\nTest 3: Scalar operations..." << std::endl;
MatrixCuda<T> E = B * T(2.5);
E.print("E = B * 2.5");
// 测试4:矩阵转置
std::cout << "\nTest 4: Matrix transpose..." << std::endl;
MatrixCuda<T> F(2, 3);
F.fill(T(1));
F.print("F (2x3)");
MatrixCuda<T> FT = F.transpose();
FT.print("F^T (3x2)");
// 测试5:元素对应乘积
std::cout << "\nTest 5: Hadamard product..." << std::endl;
MatrixCuda<T> G = MatrixCuda<T>::random(2, 2, T(0), T(1));
MatrixCuda<T> H = MatrixCuda<T>::random(2, 2, T(0), T(1));
G.print("G");
H.print("H");
MatrixCuda<T> I = G.hadamard_product(H);
I.print("G ⊙ H");
// 测试6:矩阵乘法
std::cout << "\nTest 6: Matrix multiplication..." << std::endl;
MatrixCuda<T> J(2, 3);
MatrixCuda<T> K(3, 2);
J.fill(T(1));
K.fill(T(2));
J.print("J (2x3)");
K.print("K (3x2)");
MatrixCuda<T> L = J * K;
L.print("J * K (2x2)");
std::cout << "All tests passed!" << std::endl;
} catch (const std::exception& e) {
std::cerr << "Test failed: " << e.what() << std::endl;
}
}
// 显式实例化测试函数
template void test_matrix_cuda<float>();
template void test_matrix_cuda<double>();
} // namespace qing
// ============================================================================
// 主测试程序(可选)
// ============================================================================
#ifdef TEST_MATRIX_CUDA
int main() {
std::cout << "=== MatrixCuda Test Program ===" << std::endl;
// 初始化CUDA
cudaError_t err = cudaSetDevice(0);
if (err != cudaSuccess) {
std::cerr << "Failed to set CUDA device: " << cudaGetErrorString(err) << std::endl;
return 1;
}
// 测试float版本
qing::test_matrix_cuda<float>();
std::cout << "\n";
// 测试double版本
qing::test_matrix_cuda<double>();
// 重置CUDA设备
cudaDeviceReset();
std::cout << "\n=== Test Program Finished ===" << std::endl;
return 0;
}
#endif // TEST_MATRIX_CUDA
+48
View File
@@ -0,0 +1,48 @@
#include "nn/NNBuilder.hpp"
namespace qing {
void NNBuilder::add(NeuralNetwork &layer) { nn.push_back(layer); }
std::vector<std::pair<long, long>> NNBuilder::get_shape() const {
std::vector<std::pair<long, long>> shape;
for (long j = 0; j < nn.size(); ++j) {
shape.emplace_back(nn[j].get_inputno(), nn[j].get_outputno());
}
return shape;
}
void NNBuilder::print_shape() const {
auto shape = get_shape();
for (const auto& p : shape) {
std::cout << p.first << " -> " << p.second << std::endl;
}
}
/* 前向反馈 */
std::vector<float> NNBuilder::forward(std::vector<float>& r) {
auto x = r;
for (long j=0; j< nn.size(); ++j) {
x = nn[j].forward(x);
}
return x;
}
/* 反向传播 */
std::vector<float> NNBuilder::backward(std::vector<float>& errs, float discount) {
auto e = errs;
for (long j=nn.size()-1; j>=0;--j) {
e = nn[j].backward(e);
nn[j].update(discount);
}
return e;
}
/* 计算误差 */
std::vector<float> NNBuilder::cal_err(std::vector<float>& r, std::vector<float>& t) {
auto res = r;
for (long j=0; j<r.size(); ++j) {
res[j] = t[j] - r[j];
}
return res;
}
}
+231
View File
@@ -0,0 +1,231 @@
#include "nn/NeuralNetwork.hpp"
namespace qing{
/* 构造函数 使用基本参数构造 */
NeuralNetwork::NeuralNetwork(
const long inputno, const long outputno,
const std::vector<float> &weights,
const std::vector<float> &bias,
const float learning_rate,
const ActivationFunc func_type
): inputno(inputno), outputno(outputno),
weights(inputno, outputno, weights), bias( 1, outputno, bias),
learning_rate(learning_rate),
f_type(func_type) {
;/* 进行形状检查 */
}
/* 获取输入层大小 */
long NeuralNetwork::get_inputno() const {
return this->inputno;
}
/* 获取输出层大小 */
long NeuralNetwork::get_outputno() const {
return this->outputno;
}
/* 获取权重 */
Matrx<float>& NeuralNetwork::get_weights() {
return this->weights;
}
/* 获取偏置 */
Matrx<float>& NeuralNetwork::get_bias() {
return this->bias;
}
/* 正态分布Xavier初始化 */
void NeuralNetwork::xavierNormalInit(std::vector<float> &weights, long fan_in, long fan_out)
{
/* 高质量的随机数生成器 */
static std::mt19937 generator(std::random_device{}());
/* 计算标准差 */
float sigma = std::sqrt(2.0f / (fan_in + fan_out));
/* 创建正态分布对象,均值为0,标准差为sigma */
std::normal_distribution<float> distribution(0.0f, sigma);
/* 遍历权重矩阵,进行赋值 */
long k=0;
for (long i=0; i<fan_in; ++i)
for (long j=0; j<fan_out; ++j)
weights[k++] = distribution(generator);
}
/* 均匀分布Xavier初始化函数 */
void NeuralNetwork::xavierUniformInit(std::vector<float> &weights, int fan_in, int fan_out)
{
/* 使用高质量的随机数生成器 */
static std::mt19937 generator(std::random_device{}());
/* 计算区间范围:sqrt(2 / (fan_in + fan_out)) */
float scale = std::sqrt(2.0f / (fan_in + fan_out));
/* 创建均匀分布对象,范围为[-scale, scale] */
std::uniform_real_distribution<float> distribution(-scale, scale);
/* 遍历权重矩阵,进行赋值 */
long k=0;
for (long i=0; i<fan_in; ++i)
for (long j=0; j<fan_out; ++j)
weights[k++] = distribution(generator);
}
/* 工厂模式 通过形状构建全连接层 */
NeuralNetwork NeuralNetwork::Create_in_Factory(const long inputno, const long outputno, float learning_rate, ActivationFunc func_type)
{
std::vector<float> weights(inputno * outputno);
xavierUniformInit(weights, inputno, outputno);
std::vector<float> bias(outputno);
NeuralNetwork layer(inputno, outputno, weights, bias, learning_rate, func_type);
return layer; /* FIXME: 使用移动语义 */
}
/* 前向反馈 */
std::vector<float> NeuralNetwork::forward(const std::vector<float>& x)
{
/*
* 矩阵乘法(点积)
* wi => weights inputs(column)
* wo => weights outputs(row)
*
* NOTE: 通常矩阵乘法是三层循环,这里我们用两层
*/
//std::vector<float> res(this->outputno); /* (1, outputs) */
//for (long i=0; i<this->outputno; ++i) {
// long wo = i;
// float sum=0.0;
// for (long j=0, wi=wo; j<this->inputno; ++j, wi+=this->outputno) {
// sum += x[j] * this->weights[wi];
// }
// res[i]= sum;
//}
Matrx<float>mx{1, this->inputno, x};
/*
* 为输入进行备份
*
* FIXME: 使用移动语义传入参数,并且储存
*/
this->prev_inputs = mx; /* (1, inputs) */
Matrx<float>r = mx * this->weights; /* (1, outputs) */
/* 偏置操作 */
//for (long i=0; i<this->outputno; ++i) {
// res[i]+=this->bias[i];
//}
r = r + this->bias;
/*
* 为原始计算结果进行备份
*/
this->prev_raw_inputs = r;
/*
* 使用ReLU激活函数
*
* FIXME: 建立一个算法类
*/
f_t f = getf(f_type);
std::vector<float> vec =r.get_data();
for (long i=0; i<this->outputno; ++i) {
float& v = vec[i];
v = f(v);
}
r = Matrx<float>(1, outputno, vec);
/*
* 为输出进行备份
*/
this->prev_outputs = r;
/* 返回前向反馈的结果。 (1, outputs) */
return r.get_data();
}
/* 反向传播 */
std::vector<float> NeuralNetwork::backward(const std::vector<float>& errors) /* errors => (outputs, 1) */
{
/* 匹配激活导数 */
f_t fdot = getfdot(f_type);
std::vector<float> vec = this->prev_raw_inputs.get_data();
/* 将原始输出转化为导数 */
for (long i=0; i<this->outputno; ++i) {
float& v = vec[i];
v = errors[i] * fdot(v);
}
/* 误差乘以导数 */
//std::vector<float> first(this->outputno);
//for (long i=0; i<this->outputno; ++i) {
// first[i] = errors[i] * this->prev_raw_inputs[i];
//}
Matrx<float> first{1, this->outputno, vec};
/* 更新权重梯度 => (errors * 导数) @ this->inputs => (1, outputs ) @ (inputs, 1) = (outputs, inputs) */
//this->weights_grad = std::vector<float>(this->outputno * this->inputno);
//long k = 0;
//for (long i=0; i<this->inputno; ++i) {
// for (long j=0; j<this->outputno; ++j) {
// this->weights_grad[k++] = first[j] * this->prev_inputs[i];
// }
//}
Matrx<float> mx {this->inputno, 1, this->prev_inputs.get_data()}; /* (inputs, 1) */
this->weights_grad = mx * first; /* (inputs, 1) @ (1, outputs) => (inputs, outputs) */
/*
* 更新偏置梯度
*
* NOTE: 在多批次情况下,应该沿着每一样本纵向求平均数
*/
//this->bias_grad = std::vector<float>(this->outputno);
//for (long i=0; i<this->outputno; ++i) {
// this->bias_grad[i] = first[i];
//}
this->bias_grad = first;
/*
* 计算上一层的误差责任分担,然后返回
*
* this->weights @ first => (outputs, inputs) @ (1, outputs) = (inputs, 1)
*/
//std::vector<float> uplevel_errors(this->inputno);
//for (long i=0; i<this->inputno; ++i) {
// long wii = i * this->outputno; /* 每一列的起始点 */
// float sum = 0.0f;
// for (long j=0, wi=wii; j < this->outputno; ++j, ++wi) {
// sum += this->weights[wi] * first[j];
// }
// uplevel_errors[i] = sum;
//}
Matrx<float> first1{this->outputno, 1, first.get_data()};
Matrx<float> uplevel_errs = this->weights * first1; /* (inputs, outputs) @ (outputs, 1) */
return uplevel_errs.get_data();
/* 使用移动语义返回上一层误差 */
//return std::move(uplevel_errors);
}
void NeuralNetwork::update(float discount) {
this->weights = this->weights + (this->weights_grad * this->learning_rate * discount);
this->bias = this->bias + (this->bias_grad * this->learning_rate * discount);
}
}