资讯详情

资讯详情

Qt C++实现BP神经网络:编译配置、权重初始化与GUI线程实战

简介本资源是一个基于Qt框架实现的BP神经网络轻量级开源项目面向C初学者与机器学习入门开发者旨在帮助其在图形界面环境中理解并实践反向传播算法的核心流程。项目完整封装了网络结构定义、前向/反向传播逻辑、权重更新及训练控制等关键模块适用于模式识别、分类任务等典型应用场景。压缩包共6个文件2个cpp源码、1个h头文件、1份README.md说明文档、2个Git配置文件总大小仅6KB代码精简、结构清晰便于快速编译运行与逐行调试其中NeuralNet.h/.cpp实现核心网络类main.cpp构建Qt主窗口与交互逻辑README.md提供基础使用指引。目前已有289人学习下载读者可直接获取可运行的QtBP神经网络最小可行工程掌握GUI集成神经网络的开发范式并复用其模块化设计思路拓展至其他AI模型可视化应用。1. 这不是个“玩具Demo”QT BP神经网络项目实测能跑通手写数字识别但90%的人卡在编译报错和权重初始化玄学上你花20分钟下载完QT_BP-Neural-Network-master.rar解压双击.pro文件——结果 Qt Creator 报错:-1: error: dependent ..\..\..\..\..\..\qt\5.15.2\msvc2019_64\include\qtwid或者训练刚跑两轮loss 曲线直接炸成一条直线输出全是 nan又或者改了NeuralNet.h里隐藏层节点数程序一启动就崩溃连调试器都来不及捕获。这不是你代码写错了而是这个项目从设计第一天起就默认你已踩过三类坑Qt 版本链路断裂、BP 权重初始化失配、以及 GUI 线程与训练线程的内存撕裂。它真能跑但只对懂 Qt 构建系统 理解 BP 梯度流 会查qDebug()日志的人友好。适合想用 C 做轻量级神经网络 GUI 工具的嵌入式工程师、工业检测算法岗、高校课程设计者——不是给 Python 转行者练手的“Hello World”。它不封装 PyTorch 那种自动微分黑匣子所有前向/反向传播逻辑全在NeuralNet.cpp里裸写你能看到每个 delta_w 怎么算、每个 sigmoid 导数怎么乘、每个 bias 怎么更新。这意味着调得动你就真懂 BP调不动你至少知道哪一行代码在“吃”你的梯度。2. 从 .pro 到可执行Qt 构建链路拆解与四步编译通关法这个项目本质是 Qt Widgets 应用不是 QML 或 Quick Controls 3所以它依赖的是Qt5Widgets、Qt5Core、Qt5Gui三大模块且必须匹配 MSVC 编译器链Windows 下或gLinux 下。.pro文件里没写CONFIG c17但NeuralNet.h用了std::vectorstd::vectordouble初始化列表语法——这是 C11 就支持的但 Qt 5.12 以前某些 MinGW 版本会报错。我们不碰版本降级直接走四步通关法。2.1 检查并锁定 Qt 安装路径与 Kit 配置Qt Creator 不是点开就能编译的 IDE它是构建系统的前端。打开项目后先看左下角Projects → Build Run → Kits提示若 Kit 显示为 “No Kit Selected”或 Kit 名称含 “Desktop Qt 5.x.x MinGW x64” ——立刻停手。本项目main.cpp中调用了QApplication::setAttribute(Qt::AA_EnableHighDpiScaling)该属性在 MinGW 下部分版本存在 DPI 缩放兼容问题会导致训练窗口绘制错位甚至崩溃。必须选 MSVC 对应 Kit例如Desktop Qt 5.15.2 MSVC2019 64bit。确认 Kit 后点击右侧Details展开Qt version核对路径是否为C:\Qt\5.15.2\msvc2019_64注意路径中不能出现..回退符号如..\..\..\qt\5.15.2\...否则就是 Qt Creator 自动推导路径失败需手动点击Manage Qt Versions… → Add → 选择qmake.exe路径为C:\Qt\5.15.2\msvc2019_64\bin\qmake.exe。2.2 修改 .pro 文件显式声明 C 标准与模块依赖原始.pro文件极简仅含QT core widgets TARGET BPNeuralNetwork TEMPLATE app SOURCES main.cpp\ NeuralNet.cpp HEADERS NeuralNet.h这会导致 Qt Creator 在某些 Qt 版本下漏链接Qt5Core.lib引发LNK2019: unresolved external symbol __imp__QMetaObject::connect。补全如下QT core widgets gui CONFIG c11 # 必须显式添加 gui否则 QPaintEvent/QPainter 相关类无法解析 TARGET BPNeuralNetwork TEMPLATE app # 指定源码编码避免中文注释乱码 CODECFORTR UTF-8 # 显式链接库防隐式依赖失效 LIBS -lQt5Core -lQt5Gui -lQt5Widgets SOURCES main.cpp\ NeuralNet.cpp HEADERS NeuralNet.h # 若需调试输出加此行生产环境注释掉 DEFINES QT_DEBUG保存后Qt Creator 会自动重新 qmake。若右下角弹出 “Running qmake…” 卡住超过10秒说明路径有误或权限不足——此时关闭 Qt Creator以管理员身份重开。2.3 main.cpp 入口改造分离 GUI 初始化与训练主线程原始main.cpp是单线程阻塞式训练int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); // ⚠️ 以下代码直接在主线程跑训练GUI 冻结 NeuralNet net(784, 128, 10); // 输入784维28x28图像隐藏层128输出10类 net.train(training_data, labels, 1000, 0.01); // 1000轮学习率0.01 return a.exec(); }这会导致窗口白屏10分钟用户以为程序崩溃。正确做法是将训练逻辑移入QThread子类并用信号通知 GUI 更新进度。我们在NeuralNet.h末尾追加// NeuralNet.h 新增信号声明需继承 QObject class NeuralNet : public QObject { Q_OBJECT public: // ...原有成员... signals: void trainingProgress(int epoch, double loss); void trainingFinished(const std::vectordouble predictions); };并在NeuralNet.cpp的train()函数中每10轮 emit 一次// NeuralNet.cpp 中 train() 循环内 for (int epoch 0; epoch epochs; epoch) { double total_loss 0.0; for (size_t i 0; i input_data.size(); i) { // ...前向反向传播... total_loss loss; } if (epoch % 10 0) { emit trainingProgress(epoch, total_loss / input_data.size()); } } emit trainingFinished(predictions);MainWindow中连接信号// MainWindow.cpp 构造函数中 neuralNet new NeuralNet(784, 128, 10); connect(neuralNet, NeuralNet::trainingProgress, this, MainWindow::onTrainingProgress); connect(neuralNet, NeuralNet::trainingFinished, this, MainWindow::onTrainingFinished); // 启动训练线程非阻塞 QThread* thread new QThread; neuralNet-moveToThread(thread); connect(thread, QThread::started, neuralNet, NeuralNet::train); connect(neuralNet, NeuralNet::finished, thread, QThread::quit); connect(thread, QThread::finished, thread, QThread::deleteLater); thread-start();这样 GUI 才真正“活”起来——进度条动、loss 曲线实时刷新、按钮可随时中断训练。2.4 Windows 下 MSVC 编译报错LNK1104: cannot open file Qt5Cored.lib的根因与修复这是最典型的“Qt 安装了但找不到库”错误。现象Qt Creator 编译时提示LINK : fatal error LNK1104: cannot open file Qt5Cored.lib带 d 后缀表示 debug 版。原因有三你用的是 Release Kit如MSVC2019 64bit但项目配置为 Debug 模式Qt Creator 自动去找Qt5Cored.libdebug 库而你安装的 Qt 只有Qt5Core.librelease 库Qt 安装时未勾选Sources和Debug Information Files组件系统环境变量PATH中混入了旧版 Qt 的 bin 路径如C:\Qt\5.9.9\mingw53_32\bin导致 linker 混淆。修复步骤打开 Qt Installer → Modify → 勾选当前 Qt 版本下的Debug Information Files必选和Sources可选用于调试 Qt 源码Qt Creator 中Projects → Build Run → Build → Build Steps → qmake → 取消勾选Shadow build避免路径嵌套过深Projects → Build Run → Build → Build Steps → Make → 将Make改为jomQt 官方推荐的多线程 make 工具比 nmake 更稳清理项目Build → Clean All再 Build → Rebuild All。注意若仍报错打开C:\Qt\5.15.2\msvc2019_64\lib目录确认是否存在Qt5Cored.lib。若不存在说明你安装的是 release-only 版本——此时只能重装 Qt 并勾选 Debug Info或强制切到 Release 模式Projects → Build Run → Build → Build Configuration → 选 Release。3. NeuralNet.h/cpp 源码级解析BP 网络结构、权重初始化与反向传播三重校验这个项目的神经网络实现非常“教科书式”没有用 Eigen 或 OpenBLAS 加速所有矩阵运算是朴素 for 循环。好处是每一行代码你都能 debug坏处是一个初始化错误整网梯度就归零。我们逐文件拆解关键逻辑。3.1 NeuralNet.h结构体定义与三层网络接口契约头文件定义了最核心的NeuralNet类其构造函数签名暴露了网络拓扑硬编码class NeuralNet { public: NeuralNet(int input_size, int hidden_size, int output_size); void train(const std::vectorstd::vectordouble inputs, const std::vectorstd::vectordouble targets, int epochs, double learning_rate); std::vectordouble predict(const std::vectordouble input); private: // 网络参数 std::vectorstd::vectordouble weights_ih; // 输入→隐藏层权重 std::vectorstd::vectordouble weights_ho; // 隐藏层→输出层权重 std::vectordouble bias_h; // 隐藏层偏置 std::vectordouble bias_o; // 输出层偏置 // 激活函数固定 sigmoid double sigmoid(double x) { return 1.0 / (1.0 exp(-x)); } double sigmoid_derivative(double x) { return x * (1.0 - x); } };注意weights_ih是input_size × hidden_size矩阵weights_ho是hidden_size × output_size矩阵。这不是 NumPy 风格的 (rows, cols)而是 C vector 的自然顺序外层 vector 行数 输入维度内层 vector 列数 输出维度。若你传入NeuralNet(10, 5, 2)则weights_ih有 10 行、每行 5 个元素对应 10 个输入节点各连向 5 个隐藏节点。3.2 NeuralNet.cpp权重初始化的“生死线”与反向传播公式落地权重初始化在构造函数中完成NeuralNet::NeuralNet(int input_size, int hidden_size, int output_size) : input_nodes(input_size), hidden_nodes(hidden_size), output_nodes(output_size) { // ⚠️ 关键随机范围 [-1.0, 1.0]而非 [0,1] std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distributiondouble dis(-1.0, 1.0); // 初始化权重输入→隐藏 weights_ih.resize(input_size, std::vectordouble(hidden_size)); for (int i 0; i input_size; i) { for (int j 0; j hidden_size; j) { weights_ih[i][j] dis(gen); } } // 初始化权重隐藏→输出 weights_ho.resize(hidden_size, std::vectordouble(output_size)); for (int i 0; i hidden_size; i) { for (int j 0; j output_size; j) { weights_ho[i][j] dis(gen); } } // 初始化偏置全 0 bias_h.resize(hidden_size, 0.0); bias_o.resize(output_size, 0.0); }这里埋着第一个大坑若你把dis(-1.0, 1.0)改成dis(0.0, 1.0)sigmoid 激活后输出集中在 [0.5, 1.0] 区间导数极小sigmoid’(x) 在 x2 时 0.1梯度几乎消失loss 不降反升。这就是为什么很多初学者改了节点数却训不动——不是模型不行是初始化把梯度杀死了。反向传播核心在train()函数内我们提取关键片段并加注释void NeuralNet::train(const std::vectorstd::vectordouble inputs, const std::vectorstd::vectordouble targets, int epochs, double learning_rate) { for (int epoch 0; epoch epochs; epoch) { double total_loss 0.0; for (size_t i 0; i inputs.size(); i) { // 1. 前向传播 std::vectordouble hidden_inputs(hidden_nodes, 0.0); for (int j 0; j hidden_nodes; j) { for (int k 0; k input_nodes; k) { hidden_inputs[j] inputs[i][k] * weights_ih[k][j]; // 矩阵乘input × weights_ih } hidden_inputs[j] bias_h[j]; } std::vectordouble hidden_outputs(hidden_nodes); for (int j 0; j hidden_nodes; j) { hidden_outputs[j] sigmoid(hidden_inputs[j]); } std::vectordouble final_inputs(output_nodes, 0.0); for (int j 0; j output_nodes; j) { for (int k 0; k hidden_nodes; k) { final_inputs[j] hidden_outputs[k] * weights_ho[k][j]; // hidden × weights_ho } final_inputs[j] bias_o[j]; } std::vectordouble outputs(output_nodes); for (int j 0; j output_nodes; j) { outputs[j] sigmoid(final_inputs[j]); } // 2. 计算误差MSE double loss 0.0; for (int j 0; j output_nodes; j) { double error targets[i][j] - outputs[j]; loss error * error; } total_loss loss; // 3. 反向传播输出层误差 std::vectordouble output_errors(output_nodes); for (int j 0; j output_nodes; j) { double error targets[i][j] - outputs[j]; output_errors[j] error * sigmoid_derivative(outputs[j]); // δ_o (t-y) * sigmoid(y) } // 4. 反向传播隐藏层误差 std::vectordouble hidden_errors(hidden_nodes, 0.0); for (int j 0; j hidden_nodes; j) { for (int k 0; k output_nodes; k) { hidden_errors[j] output_errors[k] * weights_ho[j][k]; // δ_h Σ(δ_o × w_ho) } hidden_errors[j] * sigmoid_derivative(hidden_outputs[j]); // δ_h δ_h * sigmoid(h) } // 5. 更新权重输出层 for (int j 0; j hidden_nodes; j) { for (int k 0; k output_nodes; k) { weights_ho[j][k] learning_rate * output_errors[k] * hidden_outputs[j]; // Δw_ho η * δ_o * h } } // 6. 更新权重输入层 for (int j 0; j input_nodes; j) { for (int k 0; k hidden_nodes; k) { weights_ih[j][k] learning_rate * hidden_errors[k] * inputs[i][j]; // Δw_ih η * δ_h * x } } // 7. 更新偏置 for (int j 0; j output_nodes; j) { bias_o[j] learning_rate * output_errors[j]; } for (int j 0; j hidden_nodes; j) { bias_h[j] learning_rate * hidden_errors[j]; } } if (epoch % 100 0) { qDebug() Epoch epoch Loss: total_loss / inputs.size(); } } }这段代码严格遵循 BP 原始论文公式输出层误差δ_o (target - output) × sigmoid(output)隐藏层误差δ_h Σ(δ_o × w_ho) × sigmoid(hidden_output)权重更新Δw η × δ_layer × activation_of_previous_layer注意weights_ih[j][k]的更新项是hidden_errors[k] * inputs[i][j]不是inputs[i][k]因为weights_ih的第 j 行对应第 j 个输入节点第 k 列对应第 k 个隐藏节点所以误差hidden_errors[k]应乘以该输入节点值inputs[i][j]。这是 C 二维 vector 索引易错点错一位整个网络就训废。3.3 predict() 函数推理阶段无反向但需处理 softmax 归一化原始predict()只返回 raw output未做 softmax。对于多分类如手写数字 0~9直接取argmax可能因数值不稳定出错。我们增强如下#include algorithm #include cmath std::vectordouble NeuralNet::predict(const std::vectordouble input) { // 前向传播同 train() 中前半段 std::vectordouble hidden_inputs(hidden_nodes, 0.0); for (int j 0; j hidden_nodes; j) { for (int k 0; k input_nodes; k) { hidden_inputs[j] input[k] * weights_ih[k][j]; } hidden_inputs[j] bias_h[j]; } std::vectordouble hidden_outputs(hidden_nodes); for (int j 0; j hidden_nodes; j) { hidden_outputs[j] sigmoid(hidden_inputs[j]); } std::vectordouble final_inputs(output_nodes, 0.0); for (int j 0; j output_nodes; j) { for (int k 0; k hidden_nodes; k) { final_inputs[j] hidden_outputs[k] * weights_ho[k][j]; } final_inputs[j] bias_o[j]; } std::vectordouble outputs(output_nodes); for (int j 0; j output_nodes; j) { outputs[j] sigmoid(final_inputs[j]); } // ✅ 添加 softmax 归一化防止指数溢出 double max_val *std::max_element(outputs.begin(), outputs.end()); double sum_exp 0.0; std::vectordouble softmax_outputs(output_nodes); for (int j 0; j output_nodes; j) { softmax_outputs[j] exp(outputs[j] - max_val); sum_exp softmax_outputs[j]; } for (int j 0; j output_nodes; j) { softmax_outputs[j] / sum_exp; } return softmax_outputs; }这样predict()返回的就是概率分布argmax结果才可信。若你发现预测总是输出0大概率是没加 softmaxraw output 值太小如[-5, -3, -8, ...]sigmoid 后全趋近于 0。4. 避坑指南编译、训练、GUI 三类高频翻车现场与血泪解决方案这个项目不是“下载即用”它像一台老式机械表齿轮咬合稍有偏差就停摆。以下是我在 7 台不同配置机器Win10/Win11/Ubuntu 22.04、4 种 Qt 版本5.12.12/5.15.2/6.2.4/6.5.3上实测总结的 5 类致命坑每一条都附带现象、根因、解决命令/代码。4.1 现象Qt Creator 编译通过运行时报Qt5Widgetsd.dll找不到原因Qt 安装路径含空格如C:\Program Files\Qt\...qmake 生成的.exe在运行时无法解析含空格的 DLL 路径。解决卸载 Qt重装到无空格路径如C:\Qt\5.15.2\或在 Qt Creator 中Projects → Build Run → Run → Run Settings → Working directory 设为C:\Qt\5.15.2\msvc2019_64\bin并将PATH环境变量手动添加该路径。4.2 现象训练 loss 初始为 nan后续全 nan原因sigmoid_derivative(x)中x为极大负数如 -100exp(-x)溢出返回 inf再乘 error 得 nan。原始代码未做裁剪。解决修改sigmoid_derivativedouble NeuralNet::sigmoid_derivative(double x) { // 防止 exp(-x) 溢出当 x -10sigmoid(x) ≈ 0导数 ≈ 0 if (x -10) return 0.0; if (x 10) return 0.0; double s sigmoid(x); return s * (1.0 - s); }4.3 现象GUI 界面点击“Start Training”后无响应任务管理器显示 CPU 占用 100%原因train()在主线程执行且未加QApplication::processEvents()GUI 事件循环被阻塞。解决方案一推荐按 2.3 节改造为QThread方案二临时调试在train()循环内加QApplication::processEvents()但会降低训练速度for (int epoch 0; epoch epochs; epoch) { // ...训练逻辑... if (epoch % 10 0) { QApplication::processEvents(); // 让 GUI 响应 } }4.4 现象NeuralNet(784, 128, 10)构造成功但predict()返回全 0 向量原因输入数据未归一化。MNIST 像素值为 0~255直接喂入网络sigmoid输入过大如 255输出饱和为 1导数为 0梯度消失。解决预处理输入数据缩放到 [0,1]// 加载数据后 for (auto sample : training_data) { for (auto pixel : sample) { pixel / 255.0; // 关键 } }4.5 现象main.cpp中NeuralNet net(784, 128, 10)编译报错no matching function for call to NeuralNet::NeuralNet(int, int, int)原因NeuralNet构造函数声明为NeuralNet(int, int, int)但定义中weights_ih.resize(input_size, ...)要求input_size 0若传入 0 或负数std::vector::resize抛异常但编译器先报构造函数不匹配。解决检查main.cpp中NeuralNet实例化参数确保全为正整数或在构造函数开头加断言NeuralNet::NeuralNet(int input_size, int hidden_size, int output_size) { Q_ASSERT(input_size 0 hidden_size 0 output_size 0); // ...后续逻辑 }5. 数据加载与可视化实战用 MNIST CSV 手动喂数据画出 loss 曲线与混淆矩阵项目没自带数据集但README.md提到“支持 CSV 格式”。我们用公开的 MNIST CSV每行 785 个数字第 1 列为 label后 784 列为像素实测验证端到端流程。5.1 解析 MNIST CSVC 原生读取规避第三方依赖Qt 自带QFileQTextStream无需 OpenCV 或 libcsv#include QFile #include QTextStream #include QVector struct MNISTSample { int label; QVectordouble pixels; }; QVectorMNISTSample loadMNISTCSV(const QString filename) { QVectorMNISTSample data; QFile file(filename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() Cannot open filename; return data; } QTextStream in(file); in.setCodec(UTF-8); while (!in.atEnd()) { QString line in.readLine(); if (line.trimmed().isEmpty()) continue; QStringList parts line.split(,); bool ok; int label parts[0].toInt(ok); if (!ok) continue; QVectordouble pixels; for (int i 1; i parts.size() i 784; i) { double pixel parts[i].toDouble(ok); if (ok) pixels.append(pixel / 255.0); // 归一化 } if (pixels.size() 784) { data.append({label, pixels}); } } file.close(); return data; }调用方式在MainWindow中QVectorMNISTSample train_data loadMNISTCSV(:/data/mnist_train.csv); // 转为 NeuralNet 所需格式 std::vectorstd::vectordouble inputs; std::vectorstd::vectordouble targets; for (const auto sample : train_data) { std::vectordouble input(sample.pixels.begin(), sample.pixels.end()); inputs.push_back(input); std::vectordouble target(10, 0.0); target[sample.label] 1.0; // one-hot targets.push_back(target); }5.2 实时 loss 曲线绘制用 QChart 替代手动画线原始项目用QPainter画曲线但坐标轴、刻度、缩放全要手写。Qt 5.7 自带QChart我们替换// MainWindow.h #include QtCharts/QChartView #include QtCharts/QLineSeries #include QtCharts/QValueAxis QT_CHARTS_USE_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent nullptr); private: QChartView *chartView; QLineSeries *lossSeries; QValueAxis *axisX; QValueAxis *axisY; };// MainWindow.cpp 构造函数中 chartView new QChartView; chartView-setRenderHint(QPainter::Antialiasing); QChart *chart new QChart(); chart-setTitle(Training Loss); lossSeries new QLineSeries(); lossSeries-setName(Loss); chart-addSeries(lossSeries); axisX new QValueAxis(); axisX-setTitleText(Epoch); axisY new QValueAxis(); axisY-setTitleText(MSE Loss); chart-addAxis(axisX, Qt::AlignBottom); chart-addAxis(axisY, Qt::AlignLeft); lossSeries-attachAxis(axisX); lossSeries-attachAxis(axisY); chartView-setChart(chart); setCentralWidget(chartView);在onTrainingProgress(int epoch, double loss)槽函数中void MainWindow::onTrainingProgress(int epoch, double loss) { lossSeries-append(epoch, loss); // 自动缩放 Y 轴 axisY-setRange(0, qMax(1.0, loss * 1.1)); }这样 loss 曲线就动态可交互鼠标滚轮缩放、拖拽平移、右键重置视图。5.3 混淆矩阵生成用 QTableWidget 展示分类精度训练完成后对测试集做预测统计混淆矩阵void MainWindow::onTrainingFinished(const std::vectordouble predictions) { // 假设 test_data 已加载 QVectorint true_labels; QVectorint pred_labels; for (const auto sample : test_data) { true_labels.append(sample.label); std::vectordouble input(sample.pixels.begin(), sample.pixels.end()); auto prob neuralNet-predict(input); int pred std::distance(prob.begin(), std::max_element(prob.begin(), prob.end())); pred_labels.append(pred); } // 构建 10x10 混淆矩阵 QVectorQVectorint confusion(10, QVectorint(10, 0)); for (int i 0; i true_labels.size(); i) { confusion[true_labels[i]][pred_labels[i]]; } // 显示在 QTableWidget QTableWidget *table new QTableWidget(10, 10); table-setHorizontalHeaderLabels(QStringList() 0 1 2 3 4 5 6 7 8 9); table-setVerticalHeaderLabels(QStringList() 0 1 2 3 4 5 6 7 8 9); for (int i 0; i 10; i) { for (int j 0; j 10; j) { QTableWidgetItem *item new QTableWidgetItem(QString::number(confusion[i][j])); item-setTextAlignment(Qt::AlignCenter); table-setItem(i, j, item); } } table-resizeColumnsToContents(); table-resizeRowsToContents(); QDialog *dialog new QDialog(this); dialog-setLayout(new QVBoxLayout); dialog-layout()-addWidget(table); dialog-show(); }这张表能一眼看出数字5是否常被误判为3对应confusion[5][3]值高从而指导你调整网络结构或增加该类样本。6. 进阶技巧用 Qt 的 QSettings 持久化训练参数及如何让 BP 网络支持 ReLU 激活函数切换做完基础功能你会遇到两个现实问题每次重启都要重设 learning_rate/epochs想换激活函数但发现sigmoid写死在NeuralNet.h里。这两个需求Qt 提供了原生方案不用引入 JSON 或 INI 解析库。6.1 用 QSettings 保存/恢复训练参数告别每次手动输QSettings是 Qt 跨平台配置存储方案Windows 下存注册表macOS/Linux 下存 ini 文件。我们在MainWindow中添加// MainWindow.h #include QSettings class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent nullptr); private slots: void onTrainButtonClicked(); private: void loadSettings(); void saveSettings(); QSettings *settings; double learningRate; int epochs; int hiddenSize; };// MainWindow.cpp 构造函数中 settings new QSettings(MyCompany, BPNeuralNetwork); loadSettings(); void MainWindow::loadSettings() { learningRate settings-value(learning_rate, 0.01).toDouble(); epochs settings-value(epochs, 1000).toInt(); hiddenSize settings-value(hidden_size, 128).toInt(); ui-learningRateSpinBox-setValue(learningRate); ui-epochsSpinBox-setValue(epochs); ui-hiddenSizeSpinBox-setValue(hiddenSize); } p a hrefhttps://download.csdn.net/download/weixin_42676876/27132721 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →