C# 从基础神经元到实现在0~9数字识别
训练图片:mnist160
测试结果:1000次训练学习率为0.1时,准确率在60%以上
学习的图片越多,训练的时候越长(比如把 epochs*10 = 10000或更高时)效果越好
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
namespace LLM
{
// 定义权重类
class Weight
{
private static Random random = new Random();
public double Value { get; set; }
public Weight()
{
Value = random.NextDouble() - 0.5;
}
}
// 定义神经元连接类
class NeuronLink
{
public Weight Weight { get; set; }
public Neuron FromNeuron { get; set; }
public Neuron ToNeuron { get; set; }
public NeuronLink(Neuron fromNeuron, Neuron toNeuron)
{
FromNeuron = fromNeuron;
ToNeuron = toNeuron;
Weight = new Weight();
}
}
// 定义神经元类
class Neuron
{
private static Random random = new Random();
public double Bias { get; set; }
public double Output { get; set; }
public double Error { get; set; }
public NeuronLink[] InputLinks { get; set; }
public Neuron(int inputCount, Neuron[] previousLayerNeurons)
{
Bias = random.NextDouble() - 0.5;
InputLinks = new NeuronLink[inputCount];
for (int i = 0; i < inputCount; i++)
{
InputLinks[i] = new NeuronLink(previousLayerNeurons[i], this);
}
}
// 激活函数(Sigmoid)
private double Sigmoid(double x)
{
return 1.0 / (1.0 + Math.Exp(-x));
}
// 计算神经元的输出
public double CalculateOutput()
{
double sum = Bias;
foreach (var link in InputLinks)
{
sum += link.FromNeuron.Output * link.Weight.Value;
}
Output = Sigmoid(sum);
return Output;
}
// 激活函数的导数
public double SigmoidDerivative()
{
return Output * (1 - Output);
}
}
// 定义层类
class Layer
{
public Neuron[] Neurons { get; set; }
public Layer(int neuronCount, Layer previousLayer)
{
Neurons = new Neuron[neuronCount];
if (previousLayer == null)
{
for (int i = 0; i < neuronCount; i++)
{
// 输入层神经元没有输入连接
Neurons[i] = new Neuron(0, new Neuron[0]);
}
}
else
{
for (int i = 0; i < neuronCount; i++)
{
Neurons[i] = new Neuron(previousLayer.Neurons.Length, previousLayer.Neurons);
}
}
}
}
// 定义神经网络类
class NeuralNetwork
{
private Layer inputLayer;
private Layer hiddenLayer;
private Layer outputLayer;
public NeuralNetwork(int inputSize, int hiddenSize, int outputSize)
{
inputLayer = new Layer(inputSize, null);
hiddenLayer = new Layer(hiddenSize, inputLayer);
outputLayer = new Layer(outputSize, hiddenLayer);
}
// 前向传播
public double[] FeedForward(double[] input)
{
// 设置输入层神经元的输出
for (int i = 0; i < inputLayer.Neurons.Length; i++)
{
inputLayer.Neurons[i].Output = input[i];
}
// 计算隐藏层神经元的输出
foreach (var neuron in hiddenLayer.Neurons)
{
neuron.CalculateOutput();
}
// 计算输出层神经元的输出
double[] outputs = new double[outputLayer.Neurons.Length];
for (int i = 0; i < outputLayer.Neurons.Length; i++)
{
outputs[i] = outputLayer.Neurons[i].CalculateOutput();
}
return outputs;
}
// 训练网络
public void Train(double[] input, double[] target, double learningRate)
{
// 前向传播
double[] output = FeedForward(input);
// 计算输出层的误差
for (int i = 0; i < outputLayer.Neurons.Length; i++)
{
outputLayer.Neurons[i].Error = (target[i] - output[i]) * outputLayer.Neurons[i].SigmoidDerivative();
}
// 反向传播到隐藏层
for (int j = 0; j < hiddenLayer.Neurons.Length; j++)
{
double errorSum = 0;
foreach (var link in hiddenLayer.Neurons[j].InputLinks)
{
errorSum += link.ToNeuron.Error * link.Weight.Value;
}
hiddenLayer.Neurons[j].Error = errorSum * hiddenLayer.Neurons[j].SigmoidDerivative();
}
// 更新输出层的权重和偏置
foreach (var neuron in outputLayer.Neurons)
{
neuron.Bias += learningRate * neuron.Error;
foreach (var link in neuron.InputLinks)
{
link.Weight.Value += learningRate * neuron.Error * link.FromNeuron.Output;
}
}
// 更新隐藏层的权重和偏置
foreach (var neuron in hiddenLayer.Neurons)
{
neuron.Bias += learningRate * neuron.Error;
foreach (var link in neuron.InputLinks)
{
link.Weight.Value += learningRate * neuron.Error * link.FromNeuron.Output;
}
}
}
}
// 定义训练数据对象类
class TrainingData
{
public double[] Input { get; set; }
public double[] Target { get; set; }
public TrainingData(double[] input, double[] target)
{
Input = input;
Target = target;
}
}
public class Program
{
//测试
public static void Main()
{
// 假设图片是 28x28 的黑白图片,输入层大小为 28x28
int inputSize = 28 * 28;
int hiddenSize = 30;
int outputSize = 10; // 识别 0 - 9 数字
NeuralNetwork neuralNetwork = new NeuralNetwork(inputSize, hiddenSize, outputSize);
// 创建训练数据对象数组
string dire = Application.StartupPath + "\\mnist160\\train\\";
string[] directories = System.IO.Directory.GetDirectories(dire);
List<TrainingData> allTrainingData = new List<TrainingData>();
string[] files = null;
foreach (string directory in directories)
{
files = System.IO.Directory.GetFiles(directory);
for (int i = 0; i < files.Length; i++)
{
// 读取图片
string imagePath = files[i];
double[] input = ReadImageAsInput(imagePath);
double[] target = new double[outputSize];
string dirname = new DirectoryInfo(directory).Name;
target[int.Parse(dirname)] = 1;
allTrainingData.Add(new TrainingData(input, target));
}
}
TrainingData[] trainingData = allTrainingData.ToArray();
// 训练网络
double learningRate = 0.1;
int epochs = 1000;
for (int epoch = 0; epoch < epochs; epoch++)
{
foreach (TrainingData data in trainingData)
{
neuralNetwork.Train(data.Input, data.Target, learningRate);
}
}
dire = Application.StartupPath + "\\mnist160\\test\\";
directories = System.IO.Directory.GetDirectories(dire);
foreach (string directory in directories)
{
files = System.IO.Directory.GetFiles(directory);
// 测试网络
foreach (var item in files)
{
string testImagePath = item;
try
{
double[] testInput = ReadImageAsInput(testImagePath);
double[] output = neuralNetwork.FeedForward(testInput);
double maxVal = output[0];
for (int i = 1; i < output.Length; i++)
{
if (output[i] > maxVal)
{
maxVal = output[i];
}
}
int predictedDigit = Array.IndexOf(output, maxVal);
//输出结果
Console.WriteLine($"Predicted digit: {testImagePath}==={predictedDigit}");
}
catch (FileNotFoundException ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
static double[] ReadImageAsInput(string imagePath)
{
if (!File.Exists(imagePath))
{
throw new FileNotFoundException($"Image file {imagePath} not found.");
}
using (Bitmap image = new Bitmap(imagePath))
{
double[] input = new double[28 * 28];
int index = 0;
for (int y = 0; y < 28; y++)
{
for (int x = 0; x < 28; x++)
{
Color pixelColor = image.GetPixel(x, y);
// 将像素值转换为 0 到 1 之间的双精度值
input[index] = (pixelColor.R + pixelColor.G + pixelColor.B) / (3.0 * 255);
index++;
}
}
return input;
}
}
}
}