Advertisement
hishlishter

Untitled

Mar 27th, 2025
373
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 14.89 KB | Source Code | 0 0
  1. using System;
  2.  
  3.     public class Program
  4.     {
  5.         // Константы: число нейронов, коэффициент обучения, число обучающих примеров
  6.         const int INPUT_NEURONS = 4;    // Признаки: кислотность, сахар, алкоголь, pH
  7.         const int HIDDEN_NEURONS = 3;
  8.         const int OUTPUT_NEURONS = 4;   // 4 типа вина
  9.         const double LEARN_RATE = 0.2;
  10.         const int MAX_SAMPLES = 16;
  11.  
  12.         // Jagged-массивы для весов (с дополнительной строкой для смещения)
  13.         double[][] wih; // Веса между входным и скрытым слоями (размер: INPUT_NEURONS+1 x HIDDEN_NEURONS)
  14.         double[][] who; // Веса между скрытым и выходным слоями (размер: HIDDEN_NEURONS+1 x OUTPUT_NEURONS)
  15.  
  16.         // Массивы для хранения значений нейронов
  17.         double[] inputs;  // Входной слой
  18.         double[] hidden;  // Скрытый слой
  19.         double[] target;  // Целевой вектор (one-hot представление)
  20.         double[] actual;  // Фактический выход сети
  21.  
  22.         // Массивы для ошибок нейронов
  23.         double[] erro;    // Ошибки на выходном слое
  24.         double[] errh;    // Ошибки на скрытом слое
  25.  
  26.         // Массив с названиями типов вина
  27.         string[] wineTypes = { "Красное", "Белое", "Розовое", "Десертное" };
  28.  
  29.         // Класс, представляющий обучающий пример
  30.         public class Sample
  31.         {
  32.             public double acidity;   // Кислотность (например, g/L)
  33.             public double sugar;     // Остаточный сахар (например, g/L)
  34.             public double alcohol;   // Содержание алкоголя (в %)
  35.             public double pH;        // Уровень pH вина
  36.             public double[] Out;     // One-hot представление типа вина
  37.  
  38.             public Sample(double acidity, double sugar, double alcohol, double pH, double[] output)
  39.             {
  40.                 this.acidity = acidity;
  41.                 this.sugar = sugar;
  42.                 this.alcohol = alcohol;
  43.                 this.pH = pH;
  44.                 this.Out = output;
  45.             }
  46.         }
  47.  
  48.         // Обучающий набор: 16 примеров (по 4 для каждого типа вина)
  49.         Sample[] samples = new Sample[]
  50.         {
  51.             // "Красное": высокая кислотность, умеренный сахар, высокий алкоголь, более низкий pH
  52.             new Sample(7.0, 2.5, 13.5, 3.3, new double[]{1.0, 0.0, 0.0, 0.0}),
  53.             new Sample(6.8, 2.6, 13.2, 3.2, new double[]{1.0, 0.0, 0.0, 0.0}),
  54.             new Sample(7.2, 2.4, 13.8, 3.4, new double[]{1.0, 0.0, 0.0, 0.0}),
  55.             new Sample(7.0, 2.5, 13.5, 3.3, new double[]{1.0, 0.0, 0.0, 0.0}),
  56.  
  57.             // "Белое": ниже кислотность, выше сахар, ниже алкоголь, немного выше pH
  58.             new Sample(6.0, 6.5, 11.0, 3.4, new double[]{0.0, 1.0, 0.0, 0.0}),
  59.             new Sample(6.2, 6.7, 11.2, 3.5, new double[]{0.0, 1.0, 0.0, 0.0}),
  60.             new Sample(6.1, 6.6, 11.1, 3.4, new double[]{0.0, 1.0, 0.0, 0.0}),
  61.             new Sample(6.0, 6.5, 11.0, 3.3, new double[]{0.0, 1.0, 0.0, 0.0}),
  62.  
  63.             // "Розовое": промежуточные значения
  64.             new Sample(6.5, 3.0, 12.5, 3.5, new double[]{0.0, 0.0, 1.0, 0.0}),
  65.             new Sample(6.4, 3.1, 12.4, 3.5, new double[]{0.0, 0.0, 1.0, 0.0}),
  66.             new Sample(6.5, 3.0, 12.5, 3.6, new double[]{0.0, 0.0, 1.0, 0.0}),
  67.             new Sample(6.6, 3.0, 12.6, 3.5, new double[]{0.0, 0.0, 1.0, 0.0}),
  68.  
  69.             // "Десертное": очень высокий сахар, низкая кислотность, низкий алкоголь, высокий pH
  70.             new Sample(5.0, 20.0, 9.0, 3.8, new double[]{0.0, 0.0, 0.0, 1.0}),
  71.             new Sample(5.1, 20.5, 9.2, 3.8, new double[]{0.0, 0.0, 0.0, 1.0}),
  72.             new Sample(5.0, 20.0, 9.0, 3.7, new double[]{0.0, 0.0, 0.0, 1.0}),
  73.             new Sample(5.1, 20.3, 9.1, 3.8, new double[]{0.0, 0.0, 0.0, 1.0})
  74.         };
  75.  
  76.         // Минимальные и максимальные значения для нормализации входных данных
  77.         static readonly double ACIDITY_MIN = 5.0;
  78.         static readonly double ACIDITY_MAX = 8.0;
  79.         static readonly double SUGAR_MIN = 2.0;
  80.         static readonly double SUGAR_MAX = 25.0;
  81.         static readonly double ALCOHOL_MIN = 9.0;
  82.         static readonly double ALCOHOL_MAX = 15.0;
  83.         static readonly double PH_MIN = 3.0;
  84.         static readonly double PH_MAX = 4.0;
  85.  
  86.         static Random rand = new Random();
  87.  
  88.         // Конструктор: инициализация массивов для весов и нейронов
  89.         public Program()
  90.         {
  91.             wih = new double[INPUT_NEURONS + 1][];
  92.             for (int i = 0; i < INPUT_NEURONS + 1; i++)
  93.             {
  94.                 wih[i] = new double[HIDDEN_NEURONS];
  95.             }
  96.  
  97.             who = new double[HIDDEN_NEURONS + 1][];
  98.             for (int i = 0; i < HIDDEN_NEURONS + 1; i++)
  99.             {
  100.                 who[i] = new double[OUTPUT_NEURONS];
  101.             }
  102.  
  103.             inputs = new double[INPUT_NEURONS];
  104.             hidden = new double[HIDDEN_NEURONS];
  105.             target = new double[OUTPUT_NEURONS];
  106.             actual = new double[OUTPUT_NEURONS];
  107.  
  108.             erro = new double[OUTPUT_NEURONS];
  109.             errh = new double[HIDDEN_NEURONS];
  110.         }
  111.  
  112.         // Инициализация весов случайными значениями в диапазоне [-0.5, 0.5]
  113.         void AssignRandomWeights()
  114.         {
  115.             for (int inp = 0; inp < INPUT_NEURONS + 1; inp++)
  116.             {
  117.                 for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
  118.                 {
  119.                     wih[inp][hid] = rand.NextDouble() - 0.5;
  120.                 }
  121.             }
  122.             for (int hid = 0; hid < HIDDEN_NEURONS + 1; hid++)
  123.             {
  124.                 for (int out = 0; out < OUTPUT_NEURONS; out++)
  125.                 {
  126.                     who[hid][out] = rand.NextDouble() - 0.5;
  127.                 }
  128.             }
  129.         }
  130.  
  131.         // Функция активации (сигмоида) и её производная
  132.         double Sigmoid(double val)
  133.         {
  134.             return 1.0 / (1.0 + Math.Exp(-val));
  135.         }
  136.  
  137.         double SigmoidDerivative(double val)
  138.         {
  139.             return val * (1.0 - val);
  140.         }
  141.  
  142.         // Нормализация входных данных (без добавления шума)
  143.         void NormalizeInputs()
  144.         {
  145.             inputs[0] = (inputs[0] - ACIDITY_MIN) / (ACIDITY_MAX - ACIDITY_MIN);
  146.             inputs[1] = (inputs[1] - SUGAR_MIN) / (SUGAR_MAX - SUGAR_MIN);
  147.             inputs[2] = (inputs[2] - ALCOHOL_MIN) / (ALCOHOL_MAX - ALCOHOL_MIN);
  148.             inputs[3] = (inputs[3] - PH_MIN) / (PH_MAX - PH_MIN);
  149.         }
  150.  
  151.         // Прямое распространение сигнала по сети
  152.         void FeedForward()
  153.         {
  154.             // Вычисление выхода скрытого слоя
  155.             for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
  156.             {
  157.                 double sum = 0.0;
  158.                 for (int inp = 0; inp < INPUT_NEURONS; inp++)
  159.                 {
  160.                     sum += inputs[inp] * wih[inp][hid];
  161.                 }
  162.                 // Добавляем смещение (последняя строка массива wih)
  163.                 sum += wih[INPUT_NEURONS][hid];
  164.                 hidden[hid] = Sigmoid(sum);
  165.             }
  166.             // Вычисление выхода выходного слоя
  167.             for (int out = 0; out < OUTPUT_NEURONS; out++)
  168.             {
  169.                 double sum = 0.0;
  170.                 for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
  171.                 {
  172.                     sum += hidden[hid] * who[hid][out];
  173.                 }
  174.                 // Добавляем смещение (последняя строка массива who)
  175.                 sum += who[HIDDEN_NEURONS][out];
  176.                 actual[out] = Sigmoid(sum);
  177.             }
  178.         }
  179.  
  180.         // Алгоритм обратного распространения ошибки
  181.         void BackPropagate()
  182.         {
  183.             // Вычисление ошибки на выходном слое
  184.             for (int out = 0; out < OUTPUT_NEURONS; out++)
  185.             {
  186.                 erro[out] = (target[out] - actual[out]) * SigmoidDerivative(actual[out]);
  187.             }
  188.             // Вычисление ошибки на скрытом слое
  189.             for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
  190.             {
  191.                 errh[hid] = 0.0;
  192.                 for (int out = 0; out < OUTPUT_NEURONS; out++)
  193.                 {
  194.                     errh[hid] += erro[out] * who[hid][out];
  195.                 }
  196.                 errh[hid] *= SigmoidDerivative(hidden[hid]);
  197.             }
  198.             // Обновление весов между скрытым и выходным слоями
  199.             for (int out = 0; out < OUTPUT_NEURONS; out++)
  200.             {
  201.                 for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
  202.                 {
  203.                     who[hid][out] += LEARN_RATE * erro[out] * hidden[hid];
  204.                 }
  205.                 // Обновление смещения
  206.                 who[HIDDEN_NEURONS][out] += LEARN_RATE * erro[out];
  207.             }
  208.             // Обновление весов между входным и скрытым слоями
  209.             for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
  210.             {
  211.                 for (int inp = 0; inp < INPUT_NEURONS; inp++)
  212.                 {
  213.                     wih[inp][hid] += LEARN_RATE * errh[hid] * inputs[inp];
  214.                 }
  215.                 // Обновление смещения
  216.                 wih[INPUT_NEURONS][hid] += LEARN_RATE * errh[hid];
  217.             }
  218.         }
  219.  
  220.         // Функция выбора индекса элемента с наибольшим значением (определяет класс)
  221.         int Action(double[] vector)
  222.         {
  223.             int sel = 0;
  224.             double max = vector[0];
  225.             for (int i = 1; i < OUTPUT_NEURONS; i++)
  226.             {
  227.                 if (vector[i] > max)
  228.                 {
  229.                     max = vector[i];
  230.                     sel = i;
  231.                 }
  232.             }
  233.             return sel;
  234.         }
  235.  
  236.         public static void Main(string[] args)
  237.         {
  238.             Program wc = new Program();
  239.             wc.AssignRandomWeights();
  240.  
  241.             int sampleIndex = 0;
  242.             double err;
  243.             // Обучение сети (10000 итераций)
  244.             for (int step = 0; step < 10000; step++)
  245.             {
  246.                 sampleIndex = (sampleIndex + 1) % MAX_SAMPLES;
  247.                 Sample s = wc.samples[sampleIndex];
  248.                 // Заполняем входной вектор: порядок – кислотность, сахар, алкоголь, pH
  249.                 wc.inputs[0] = s.acidity;
  250.                 wc.inputs[1] = s.sugar;
  251.                 wc.inputs[2] = s.alcohol;
  252.                 wc.inputs[3] = s.pH;
  253.                 // Нормализация входных данных
  254.                 wc.NormalizeInputs();
  255.                 // Копирование целевого вектора
  256.                 for (int i = 0; i < OUTPUT_NEURONS; i++)
  257.                 {
  258.                     wc.target[i] = s.Out[i];
  259.                 }
  260.                 wc.FeedForward();
  261.                 err = 0.0;
  262.                 for (int i = 0; i < OUTPUT_NEURONS; i++)
  263.                 {
  264.                     double diff = s.Out[i] - wc.actual[i];
  265.                     err += diff * diff;
  266.                 }
  267.                 err = 0.5 * err;
  268.                 if (step % 1000 == 0)
  269.                 {
  270.                     Console.WriteLine("step = " + step + " mse = " + err);
  271.                 }
  272.                 wc.BackPropagate();
  273.             }
  274.  
  275.             Console.WriteLine();
  276.             int correct = 0;
  277.             // Проверка сети на обучающих примерах
  278.             for (int i = 0; i < MAX_SAMPLES; i++)
  279.             {
  280.                 Sample s = wc.samples[i];
  281.                 wc.inputs[0] = s.acidity;
  282.                 wc.inputs[1] = s.sugar;
  283.                 wc.inputs[2] = s.alcohol;
  284.                 wc.inputs[3] = s.pH;
  285.                 wc.NormalizeInputs();
  286.                 for (int j = 0; j < OUTPUT_NEURONS; j++)
  287.                 {
  288.                     wc.target[j] = s.Out[j];
  289.                 }
  290.                 wc.FeedForward();
  291.                 int predicted = wc.Action(wc.actual);
  292.                 int expected = wc.Action(wc.target);
  293.                 if (predicted != expected)
  294.                 {
  295.                     Console.WriteLine("Input: " + s.acidity + " " + s.sugar + " "
  296.                         + s.alcohol + " " + s.pH +
  297.                         " predicted: " + wc.wineTypes[predicted] +
  298.                         " expected: " + wc.wineTypes[expected]);
  299.                 }
  300.                 else
  301.                 {
  302.                     correct++;
  303.                 }
  304.             }
  305.             Console.WriteLine("Network is " + ((float)correct / MAX_SAMPLES * 100.0) + "% correct\n");
  306.  
  307.             // Дополнительное тестирование с новыми входными данными:
  308.             // Порядок входов: кислотность, сахар, алкоголь, pH
  309.             double[][] testInputs = new double[][]
  310.             {
  311.                 new double[] {7.0, 2.5, 13.5, 3.3},    // ожидается "Красное"
  312.                 new double[] {6.0, 6.5, 11.0, 3.4},     // ожидается "Белое"
  313.                 new double[] {6.5, 3.0, 12.5, 3.5},     // ожидается "Розовое"
  314.                 new double[] {5.0, 20.0, 9.0, 3.8}      // ожидается "Десертное"
  315.             };
  316.             foreach (double[] test in testInputs)
  317.             {
  318.                 wc.inputs[0] = test[0];
  319.                 wc.inputs[1] = test[1];
  320.                 wc.inputs[2] = test[2];
  321.                 wc.inputs[3] = test[3];
  322.                 wc.NormalizeInputs();
  323.                 wc.FeedForward();
  324.                 int index = wc.Action(wc.actual);
  325.                 Console.WriteLine("Input: [" + test[0] + ", " + test[1] + ", " + test[2] + ", " + test[3]
  326.                     + "] -> " + wc.wineTypes[index]);
  327.             }
  328.         }
  329. }
  330.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement