Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- public class Program
- {
- // Константы: число нейронов, коэффициент обучения, число обучающих примеров
- const int INPUT_NEURONS = 4; // Признаки: кислотность, сахар, алкоголь, pH
- const int HIDDEN_NEURONS = 3;
- const int OUTPUT_NEURONS = 4; // 4 типа вина
- const double LEARN_RATE = 0.2;
- const int MAX_SAMPLES = 16;
- // Jagged-массивы для весов (с дополнительной строкой для смещения)
- double[][] wih; // Веса между входным и скрытым слоями (размер: INPUT_NEURONS+1 x HIDDEN_NEURONS)
- double[][] who; // Веса между скрытым и выходным слоями (размер: HIDDEN_NEURONS+1 x OUTPUT_NEURONS)
- // Массивы для хранения значений нейронов
- double[] inputs; // Входной слой
- double[] hidden; // Скрытый слой
- double[] target; // Целевой вектор (one-hot представление)
- double[] actual; // Фактический выход сети
- // Массивы для ошибок нейронов
- double[] erro; // Ошибки на выходном слое
- double[] errh; // Ошибки на скрытом слое
- // Массив с названиями типов вина
- string[] wineTypes = { "Красное", "Белое", "Розовое", "Десертное" };
- // Класс, представляющий обучающий пример
- public class Sample
- {
- public double acidity; // Кислотность (например, g/L)
- public double sugar; // Остаточный сахар (например, g/L)
- public double alcohol; // Содержание алкоголя (в %)
- public double pH; // Уровень pH вина
- public double[] Out; // One-hot представление типа вина
- public Sample(double acidity, double sugar, double alcohol, double pH, double[] output)
- {
- this.acidity = acidity;
- this.sugar = sugar;
- this.alcohol = alcohol;
- this.pH = pH;
- this.Out = output;
- }
- }
- // Обучающий набор: 16 примеров (по 4 для каждого типа вина)
- Sample[] samples = new Sample[]
- {
- // "Красное": высокая кислотность, умеренный сахар, высокий алкоголь, более низкий pH
- new Sample(7.0, 2.5, 13.5, 3.3, new double[]{1.0, 0.0, 0.0, 0.0}),
- new Sample(6.8, 2.6, 13.2, 3.2, new double[]{1.0, 0.0, 0.0, 0.0}),
- new Sample(7.2, 2.4, 13.8, 3.4, new double[]{1.0, 0.0, 0.0, 0.0}),
- new Sample(7.0, 2.5, 13.5, 3.3, new double[]{1.0, 0.0, 0.0, 0.0}),
- // "Белое": ниже кислотность, выше сахар, ниже алкоголь, немного выше pH
- new Sample(6.0, 6.5, 11.0, 3.4, new double[]{0.0, 1.0, 0.0, 0.0}),
- new Sample(6.2, 6.7, 11.2, 3.5, new double[]{0.0, 1.0, 0.0, 0.0}),
- new Sample(6.1, 6.6, 11.1, 3.4, new double[]{0.0, 1.0, 0.0, 0.0}),
- new Sample(6.0, 6.5, 11.0, 3.3, new double[]{0.0, 1.0, 0.0, 0.0}),
- // "Розовое": промежуточные значения
- new Sample(6.5, 3.0, 12.5, 3.5, new double[]{0.0, 0.0, 1.0, 0.0}),
- new Sample(6.4, 3.1, 12.4, 3.5, new double[]{0.0, 0.0, 1.0, 0.0}),
- new Sample(6.5, 3.0, 12.5, 3.6, new double[]{0.0, 0.0, 1.0, 0.0}),
- new Sample(6.6, 3.0, 12.6, 3.5, new double[]{0.0, 0.0, 1.0, 0.0}),
- // "Десертное": очень высокий сахар, низкая кислотность, низкий алкоголь, высокий pH
- new Sample(5.0, 20.0, 9.0, 3.8, new double[]{0.0, 0.0, 0.0, 1.0}),
- new Sample(5.1, 20.5, 9.2, 3.8, new double[]{0.0, 0.0, 0.0, 1.0}),
- new Sample(5.0, 20.0, 9.0, 3.7, new double[]{0.0, 0.0, 0.0, 1.0}),
- new Sample(5.1, 20.3, 9.1, 3.8, new double[]{0.0, 0.0, 0.0, 1.0})
- };
- // Минимальные и максимальные значения для нормализации входных данных
- static readonly double ACIDITY_MIN = 5.0;
- static readonly double ACIDITY_MAX = 8.0;
- static readonly double SUGAR_MIN = 2.0;
- static readonly double SUGAR_MAX = 25.0;
- static readonly double ALCOHOL_MIN = 9.0;
- static readonly double ALCOHOL_MAX = 15.0;
- static readonly double PH_MIN = 3.0;
- static readonly double PH_MAX = 4.0;
- static Random rand = new Random();
- // Конструктор: инициализация массивов для весов и нейронов
- public Program()
- {
- wih = new double[INPUT_NEURONS + 1][];
- for (int i = 0; i < INPUT_NEURONS + 1; i++)
- {
- wih[i] = new double[HIDDEN_NEURONS];
- }
- who = new double[HIDDEN_NEURONS + 1][];
- for (int i = 0; i < HIDDEN_NEURONS + 1; i++)
- {
- who[i] = new double[OUTPUT_NEURONS];
- }
- inputs = new double[INPUT_NEURONS];
- hidden = new double[HIDDEN_NEURONS];
- target = new double[OUTPUT_NEURONS];
- actual = new double[OUTPUT_NEURONS];
- erro = new double[OUTPUT_NEURONS];
- errh = new double[HIDDEN_NEURONS];
- }
- // Инициализация весов случайными значениями в диапазоне [-0.5, 0.5]
- void AssignRandomWeights()
- {
- for (int inp = 0; inp < INPUT_NEURONS + 1; inp++)
- {
- for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
- {
- wih[inp][hid] = rand.NextDouble() - 0.5;
- }
- }
- for (int hid = 0; hid < HIDDEN_NEURONS + 1; hid++)
- {
- for (int out = 0; out < OUTPUT_NEURONS; out++)
- {
- who[hid][out] = rand.NextDouble() - 0.5;
- }
- }
- }
- // Функция активации (сигмоида) и её производная
- double Sigmoid(double val)
- {
- return 1.0 / (1.0 + Math.Exp(-val));
- }
- double SigmoidDerivative(double val)
- {
- return val * (1.0 - val);
- }
- // Нормализация входных данных (без добавления шума)
- void NormalizeInputs()
- {
- inputs[0] = (inputs[0] - ACIDITY_MIN) / (ACIDITY_MAX - ACIDITY_MIN);
- inputs[1] = (inputs[1] - SUGAR_MIN) / (SUGAR_MAX - SUGAR_MIN);
- inputs[2] = (inputs[2] - ALCOHOL_MIN) / (ALCOHOL_MAX - ALCOHOL_MIN);
- inputs[3] = (inputs[3] - PH_MIN) / (PH_MAX - PH_MIN);
- }
- // Прямое распространение сигнала по сети
- void FeedForward()
- {
- // Вычисление выхода скрытого слоя
- for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
- {
- double sum = 0.0;
- for (int inp = 0; inp < INPUT_NEURONS; inp++)
- {
- sum += inputs[inp] * wih[inp][hid];
- }
- // Добавляем смещение (последняя строка массива wih)
- sum += wih[INPUT_NEURONS][hid];
- hidden[hid] = Sigmoid(sum);
- }
- // Вычисление выхода выходного слоя
- for (int out = 0; out < OUTPUT_NEURONS; out++)
- {
- double sum = 0.0;
- for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
- {
- sum += hidden[hid] * who[hid][out];
- }
- // Добавляем смещение (последняя строка массива who)
- sum += who[HIDDEN_NEURONS][out];
- actual[out] = Sigmoid(sum);
- }
- }
- // Алгоритм обратного распространения ошибки
- void BackPropagate()
- {
- // Вычисление ошибки на выходном слое
- for (int out = 0; out < OUTPUT_NEURONS; out++)
- {
- erro[out] = (target[out] - actual[out]) * SigmoidDerivative(actual[out]);
- }
- // Вычисление ошибки на скрытом слое
- for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
- {
- errh[hid] = 0.0;
- for (int out = 0; out < OUTPUT_NEURONS; out++)
- {
- errh[hid] += erro[out] * who[hid][out];
- }
- errh[hid] *= SigmoidDerivative(hidden[hid]);
- }
- // Обновление весов между скрытым и выходным слоями
- for (int out = 0; out < OUTPUT_NEURONS; out++)
- {
- for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
- {
- who[hid][out] += LEARN_RATE * erro[out] * hidden[hid];
- }
- // Обновление смещения
- who[HIDDEN_NEURONS][out] += LEARN_RATE * erro[out];
- }
- // Обновление весов между входным и скрытым слоями
- for (int hid = 0; hid < HIDDEN_NEURONS; hid++)
- {
- for (int inp = 0; inp < INPUT_NEURONS; inp++)
- {
- wih[inp][hid] += LEARN_RATE * errh[hid] * inputs[inp];
- }
- // Обновление смещения
- wih[INPUT_NEURONS][hid] += LEARN_RATE * errh[hid];
- }
- }
- // Функция выбора индекса элемента с наибольшим значением (определяет класс)
- int Action(double[] vector)
- {
- int sel = 0;
- double max = vector[0];
- for (int i = 1; i < OUTPUT_NEURONS; i++)
- {
- if (vector[i] > max)
- {
- max = vector[i];
- sel = i;
- }
- }
- return sel;
- }
- public static void Main(string[] args)
- {
- Program wc = new Program();
- wc.AssignRandomWeights();
- int sampleIndex = 0;
- double err;
- // Обучение сети (10000 итераций)
- for (int step = 0; step < 10000; step++)
- {
- sampleIndex = (sampleIndex + 1) % MAX_SAMPLES;
- Sample s = wc.samples[sampleIndex];
- // Заполняем входной вектор: порядок – кислотность, сахар, алкоголь, pH
- wc.inputs[0] = s.acidity;
- wc.inputs[1] = s.sugar;
- wc.inputs[2] = s.alcohol;
- wc.inputs[3] = s.pH;
- // Нормализация входных данных
- wc.NormalizeInputs();
- // Копирование целевого вектора
- for (int i = 0; i < OUTPUT_NEURONS; i++)
- {
- wc.target[i] = s.Out[i];
- }
- wc.FeedForward();
- err = 0.0;
- for (int i = 0; i < OUTPUT_NEURONS; i++)
- {
- double diff = s.Out[i] - wc.actual[i];
- err += diff * diff;
- }
- err = 0.5 * err;
- if (step % 1000 == 0)
- {
- Console.WriteLine("step = " + step + " mse = " + err);
- }
- wc.BackPropagate();
- }
- Console.WriteLine();
- int correct = 0;
- // Проверка сети на обучающих примерах
- for (int i = 0; i < MAX_SAMPLES; i++)
- {
- Sample s = wc.samples[i];
- wc.inputs[0] = s.acidity;
- wc.inputs[1] = s.sugar;
- wc.inputs[2] = s.alcohol;
- wc.inputs[3] = s.pH;
- wc.NormalizeInputs();
- for (int j = 0; j < OUTPUT_NEURONS; j++)
- {
- wc.target[j] = s.Out[j];
- }
- wc.FeedForward();
- int predicted = wc.Action(wc.actual);
- int expected = wc.Action(wc.target);
- if (predicted != expected)
- {
- Console.WriteLine("Input: " + s.acidity + " " + s.sugar + " "
- + s.alcohol + " " + s.pH +
- " predicted: " + wc.wineTypes[predicted] +
- " expected: " + wc.wineTypes[expected]);
- }
- else
- {
- correct++;
- }
- }
- Console.WriteLine("Network is " + ((float)correct / MAX_SAMPLES * 100.0) + "% correct\n");
- // Дополнительное тестирование с новыми входными данными:
- // Порядок входов: кислотность, сахар, алкоголь, pH
- double[][] testInputs = new double[][]
- {
- new double[] {7.0, 2.5, 13.5, 3.3}, // ожидается "Красное"
- new double[] {6.0, 6.5, 11.0, 3.4}, // ожидается "Белое"
- new double[] {6.5, 3.0, 12.5, 3.5}, // ожидается "Розовое"
- new double[] {5.0, 20.0, 9.0, 3.8} // ожидается "Десертное"
- };
- foreach (double[] test in testInputs)
- {
- wc.inputs[0] = test[0];
- wc.inputs[1] = test[1];
- wc.inputs[2] = test[2];
- wc.inputs[3] = test[3];
- wc.NormalizeInputs();
- wc.FeedForward();
- int index = wc.Action(wc.actual);
- Console.WriteLine("Input: [" + test[0] + ", " + test[1] + ", " + test[2] + ", " + test[3]
- + "] -> " + wc.wineTypes[index]);
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement