首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C 語言實作卷積神經網絡 (CNN) 完整架構解析

     編輯:關於C

前言

本系列第二篇,聚焦 CNN 整體網路架構與前向傳播實作。參考 LeNet-5 並簡化調整:移除輸入填邊、C3 採全連接卷積、省略 C5/F6 層,直接由 S4 接全連接輸出層。適用於 MNIST 手寫數字辨識任務。

網路架構總覽

層名類型輸入核心參數輸出
C1卷積1×28×286×5×5 核, Sigmoid6×24×24
S2平均池化6×24×242×2 窗口, stride=26×12×12
C3全連接卷積6×12×126×12 個 5×5 核, Sigmoid12×8×8
S4平均池化12×8×82×2 窗口, stride=212×4×4
O5全連接192 (12×4×4)192×10 權重, Sigmoid10 (類別機率)

關鍵資料結構

// 卷積層
typedef struct {
    int inW, inH, kSize, inCh, outCh;
    bool fullConnect;
    bool *connectMap;      // 非全連接時的連接模式
    float ****kernels;     // [inCh][outCh][kSize][kSize]
    float ****dkernels;    // 梯度
    float *bias;           // [outCh]
    float ***v, ***y, ***d; // 前向/反向中間值
} ConvLayer;

// 池化層
typedef struct {
    int inW, inH, kSize, inCh, outCh, poolType; // 0=Avg, 1=Max
    float *bias;
    float ***y, ***d;
} PoolLayer;

// 全連接輸出層
typedef struct {
    int inDim, outDim;
    float **weights;       // [inDim][outDim]
    float *bias;           // [outDim]
    float *v, *y, *d;
    bool fullConnect;
} OutputLayer;

// 整體網路
typedef struct {
    ConvLayer *C1, *C3;
    PoolLayer *S2, *S4;
    OutputLayer *O5;
    float *error, *loss;
} CNN;

前向傳播核心流程 (cnnff)

C1 卷積層

// 輸入: float ***input (inCh x inH x inW)
for (int oc = 0; oc < C1->outCh; ++oc) {
    for (int ic = 0; ic < C1->inCh; ++ic) {
        float **conv_out = conv2d(C1->kernels[ic][oc], C1->kSize,
                                  input[ic], C1->inH, C1->inW, VALID);
        add_matrix(C1->v[oc], C1->v[oc], conv_out, outH, outW);
        free_matrix(conv_out);
    }
    // 加 bias 並通過 Sigmoid
    for (int r = 0; r < outH; ++r)
        for (int c = 0; c < outW; ++c)
            C1->y[oc][r][c] = sigmoid(C1->v[oc][r][c] + C1->bias[oc]);
}

S2 平均池化

for (int ch = 0; ch < S2->outCh; ++ch)
    avg_pooling(S2->y[ch], outH, outW, C1->y[ch], inH, inW, S2->kSize);

C3 全連接卷積 (6 in → 12 out)

for (int oc = 0; oc < C3->outCh; ++oc) {
    for (int ic = 0; ic < C3->inCh; ++ic) {
        float **conv_out = conv2d(C3->kernels[ic][oc], C3->kSize,
                                  S2->y[ic], inH, inW, VALID);
        add_matrix(C3->v[oc], C3->v[oc], conv_out, outH, outW);
        free_matrix(conv_out);
    }
    for (int r = 0; r < outH; ++r)
        for (int c = 0; c < outW; ++c)
            C3->y[oc][r][c] = sigmoid(C3->v[oc][r][c] + C3->bias[oc]);
}

S4 平均池化 → 展平

for (int ch = 0; ch < S4->outCh; ++ch)
    avg_pooling(S4->y[ch], outH, outW, C3->y[ch], inH, inW, S4->kSize);

// 展平為 1D 向量 (192)
float *flat = malloc(O5->inDim * sizeof(float));
int idx = 0;
for (int ch = 0; ch < S4->outCh; ++ch)
    for (int r = 0; r < outH; ++r)
        for (int c = 0; c < outW; ++c)
            flat[idx++] = S4->y[ch][r][c];

O5 全連接輸出層

// 矩陣向量乘法: v = flat * W + bias
mat_vec_mul(O5->v, flat, O5->weights, O5->bias, O5->inDim, O5->outDim);
for (int i = 0; i < O5->outDim; ++i)
    O5->y[i] = sigmoid(O5->v[i]);
free(flat);

激活函數與工具函式

float sigmoid(float x) { return 1.0f / (1.0f + expf(-x)); }

// 2D 卷積 (VALID padding)
float **conv2d(float **kernel, int k, float **input, int H, int W, int mode) {
    int outH = H - k + 1, outW = W - k + 1;
    float **out = alloc_matrix(outH, outW);
    for (int r = 0; r < outH; ++r)
        for (int c = 0; c < outW; ++c) {
            float sum = 0;
            for (int kr = 0; kr < k; ++kr)
                for (int kc = 0; kc < k; ++kc)
                    sum += kernel[kr][kc] * input[r+kr][c+kc];
            out[r][c] = sum;
        }
    return out;
}

// 平均池化
void avg_pooling(float **out, int oH, int oW, float **in, int iH, int iW, int k) {
    for (int r = 0; r < oH; ++r)
        for (int c = 0; c < oW; ++c) {
            float sum = 0;
            for (int kr = 0; kr < k; ++kr)
                for (int kc = 0; kc < k; ++kc)
                    sum += in[r*k+kr][c*k+kc];
            out[r][c] = sum / (k*k);
        }
}

初始化與訓練超參數

typedef struct { int epochs; float alpha; } TrainOpts;

void cnn_init(CNN *net) {
    // 依結構體逐層 malloc,權重用 Xavier/He 初始化
    // bias 歸零
}

TrainOpts opts = { .epochs = 30, .alpha = 0.01f };

總結與下一步

本文完整定義了 CNN 的層級結構、資料結構與前向傳播流程。下一篇將實作反向傳播 (BP) 算法:計算輸出層誤差、逐層反傳梯度、更新權重與 bias,並結合 MNIST 資料集完成端到端訓練與測試。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved