nrtsb

nrtsb

Пикабушник
Дата рождения: 16 июля
в топе авторов на 651 месте
147 рейтинг 0 подписчиков 3 подписки 12 постов 0 в горячем
1

Как я написал нейросеть на Паскале

Все привыкли, что PascalABC.NET — это «введите два числа» и «нарисовать домик». А я решил: а почему бы не сделать полноценную нейросеть?

Да-да, полносвязный перцептрон с обратным распространением ошибки. На том самом языке, где begin и end занимают половину экрана.

Что умеет:

Обучается на задаче XOR — та самая классика, с которой нейросети справились только после появления скрытых слоёв. 2 входа → 4 нейрона → 1 выход. Результат: идеально разделяет нелинейную задачу.

Аппроксимирует синус — сеть 1→16→1 учится предсказывать sin(x) после 8000 эпох. Сначала тупит, потом выдает с точностью до сотых.

Сохраняет/загружает веса — натренировал, сохранил в файл, загрузил — пользуйся.

Три функции активации: сигмоида, tanh, ReLU.

А чтобы было не скучно, добавил туда же инженерный калькулятор. Потому что нейросеть нейросетью, а синус 30 градусов посчитать иногда важнее. Тригонометрия, логарифмы, факториалы — чтобы оправдать название «инженерный».

Весь код — одна программа на PascalABC.NET. Никаких сторонних библиотек.

Полный код прикрепляю ниже:

program MathModCalc;
uses System, System.IO, System.Collections.Generic;
type
TRealArray = array of real;

TActivation = (actSigmoid, actTanh, actReLU);
TLayer = class
private
W: array[,] of real;
b: TRealArray;
n_in, n_out: integer;
last_input: TRealArray;
last_output: TRealArray;
activation: TActivation;
class function Sigmoid(x: real): real;
class function DSigmoid(y: real): real;
class function TanhF(x: real): real;
class function DTanh(y: real): real;
class function Relu(x: real): real;
class function DRelu(y: real): real;
function Activate(x: real): real;
function Derivative(y: real): real;
public
constructor Create(inputSize, outputSize: integer; act: TActivation);
procedure InitRandom;
function Run(const input: TRealArray): TRealArray;
function Backward(const gradient: TRealArray; lr: real): TRealArray;
procedure SaveToBinary(var bw: BinaryWriter);
procedure LoadFromBinary(var br: BinaryReader);
end;
TNeuralNetwork = class
private
layers: List<TLayer>;
public
constructor Create;
procedure AddLayer(inputSize, outputSize: integer; act: TActivation);
function Predict(const input: TRealArray): TRealArray;
function Train(const inputs, targets: array of TRealArray;
epochs: integer; lr: real): TRealArray;
procedure SaveToFile(const filename: string);
procedure LoadFromFile(const filename: string);
end;
function MakeVec(const a: array of real): TRealArray;
var i: integer;
begin
SetLength(Result, a.Length);
for i := 0 to a.Length - 1 do
Result[i] := a[i];
end;
{ TLayer }
class function TLayer.Sigmoid(x: real): real;
begin
if x < -45.0 then Result := 0.0
else if x > 45.0 then Result := 1.0
else Result := 1.0 / (1.0 + Exp(-x));
end;
class function TLayer.DSigmoid(y: real): real;
begin
Result := y * (1.0 - y);
end;
class function TLayer.TanhF(x: real): real;
var e2x: real;
begin
if x < -20.0 then Result := -1.0
else if x > 20.0 then Result := 1.0
else
begin
e2x := Exp(2.0 * x);
Result := (e2x - 1.0) / (e2x + 1.0);
end;
end;
class function TLayer.DTanh(y: real): real;
begin
Result := 1.0 - y * y;
end;
class function TLayer.Relu(x: real): real;
begin
if x > 0.0 then Result := x else Result := 0.0;
end;
class function TLayer.DRelu(y: real): real;
begin
if y > 0.0 then Result := 1.0 else Result := 0.0;
end;
function TLayer.Activate(x: real): real;
begin
case activation of
actSigmoid: Result := Sigmoid(x);
actTanh: Result := TanhF(x);
actReLU: Result := Relu(x);
else Result := Sigmoid(x);
end;
end;
function TLayer.Derivative(y: real): real;
begin
case activation of
actSigmoid: Result := DSigmoid(y);
actTanh: Result := DTanh(y);
actReLU: Result := DRelu(y);
else Result := DSigmoid(y);
end;
end;
constructor TLayer.Create(inputSize, outputSize: integer; act: TActivation);
begin
n_in := inputSize;
n_out := outputSize;
activation := act;
SetLength(W, n_out, n_in);
SetLength(b, n_out);
SetLength(last_input, n_in);
SetLength(last_output, n_out);
InitRandom;
end;
procedure TLayer.InitRandom;
var i, j: integer;
limit: real;
rnd: Random;
begin
rnd := new Random;
limit := Sqrt(6.0 / (n_in + n_out));
for i := 0 to n_out - 1 do
begin
for j := 0 to n_in - 1 do
W[i, j] := (rnd.NextDouble * 2.0 - 1.0) * limit;
b[i] := (rnd.NextDouble * 2.0 - 1.0) * limit;
end;
end;
function TLayer.Run(const input: TRealArray): TRealArray;
var i, j: integer;
s: real;
begin
for j := 0 to n_in - 1 do
last_input[j] := input[j];
for i := 0 to n_out - 1 do
begin
s := b[i];
for j := 0 to n_in - 1 do
s := s + W[i, j] * input[j];
last_output[i] := Activate(s);
end;
Result := last_output;
end;
function TLayer.Backward(const gradient: TRealArray; lr: real): TRealArray;
var i, j: integer;
delta, prev_grad: TRealArray;
begin
SetLength(delta, n_out);
SetLength(prev_grad, n_in);
for i := 0 to n_out - 1 do
delta[i] := gradient[i] * Derivative(last_output[i]);
for j := 0 to n_in - 1 do
begin
prev_grad[j] := 0.0;
for i := 0 to n_out - 1 do
prev_grad[j] := prev_grad[j] + W[i, j] * delta[i];
end;
for i := 0 to n_out - 1 do
begin
for j := 0 to n_in - 1 do
W[i, j] := W[i, j] - lr * delta[i] * last_input[j];
b[i] := b[i] - lr * delta[i];
end;
Result := prev_grad;
end;
procedure TLayer.SaveToBinary(var bw: BinaryWriter);
var i, j: integer;
begin
bw.Write(n_in);
bw.Write(n_out);
bw.Write(Ord(activation));
for i := 0 to n_out - 1 do
begin
for j := 0 to n_in - 1 do
bw.Write(W[i, j]);
bw.Write(b[i]);
end;
end;
procedure TLayer.LoadFromBinary(var br: BinaryReader);
var i, j: integer;
begin
for i := 0 to n_out - 1 do
begin
for j := 0 to n_in - 1 do
W[i, j] := br.ReadDouble;
b[i] := br.ReadDouble;
end;
end;
constructor TNeuralNetwork.Create;
begin
layers := new List<TLayer>;
end;
procedure TNeuralNetwork.AddLayer(inputSize, outputSize: integer; act: TActivation);
begin
layers.Add(new TLayer(inputSize, outputSize, act));
end;
function TNeuralNetwork.Predict(const input: TRealArray): TRealArray;
var i: integer;
begin
Result := input;
for i := 0 to layers.Count - 1 do
Result := layers[i].Run(Result);
end;
function TNeuralNetwork.Train(const inputs, targets: array of TRealArray;
epochs: integer; lr: real): TRealArray;
var
epoch, sample, i, ns: integer;
output, grad, errors: TRealArray;
totalError, mse: real;
begin
ns := inputs.Length;
SetLength(errors, epochs);
for epoch := 0 to epochs - 1 do
begin
totalError := 0.0;
for sample := 0 to ns - 1 do
begin
output := Predict(inputs[sample]);
for i := 0 to output.Length - 1 do
totalError := totalError + Sqr(output[i] - targets[sample][i]);
SetLength(grad, output.Length);
for i := 0 to output.Length - 1 do
grad[i] := output[i] - targets[sample][i];
for i := layers.Count - 1 downto 0 do
grad := layers[i].Backward(grad, lr);
end;
mse := totalError / ns;
errors[epoch] := mse;
if epoch mod 500 = 0 then
Writeln(' Эпоха ', epoch:5, ': MSE = ', mse:0:8);
end;
Result := errors;
end;
procedure TNeuralNetwork.SaveToFile(const filename: string);
var fs: FileStream;
bw: BinaryWriter;
i: integer;
begin
fs := new FileStream(filename, FileMode.Create);
bw := new BinaryWriter(fs);
try
bw.Write(layers.Count);
for i := 0 to layers.Count - 1 do
layers[i].SaveToBinary(bw);
finally
bw.Close;
fs.Close;
end;
Writeln('Модель сохранена в файл: ', filename);
end;
procedure TNeuralNetwork.LoadFromFile(const filename: string);
var fs: FileStream;
br: BinaryReader;
cnt, i: integer;
begin
fs := new FileStream(filename, FileMode.Open);
br := new BinaryReader(fs);
try
cnt := br.ReadInt32;
if cnt <> layers.Count then
Writeln('Предупреждение: число слоев в файле не совпадает');
for i := 0 to Min(cnt, layers.Count) - 1 do
layers[i].LoadFromBinary(br);
finally
br.Close;
fs.Close;
end;
Writeln('Модель загружена из файла: ', filename);
end;
procedure DemoXOR;
var
nn: TNeuralNetwork;
inputs, targets: array of TRealArray;
i: integer;
pred: TRealArray;
errVec: TRealArray;
begin
Writeln;
Writeln('-------------------------------------------');
Writeln('ДЕМО 1: XOR (исключающее ИЛИ)');
Writeln('Архитектура: 2 -> 4 (tanh) -> 1 (sigmoid)');
Writeln('-------------------------------------------');
Writeln;
nn := new TNeuralNetwork;
nn.AddLayer(2, 4, actTanh);
nn.AddLayer(4, 1, actSigmoid);
SetLength(inputs, 4);
SetLength(targets, 4);
SetLength(inputs[0], 2); inputs[0][0] := 0; inputs[0][1] := 0;
SetLength(targets[0], 1); targets[0][0] := 0;
SetLength(inputs[1], 2); inputs[1][0] := 0; inputs[1][1] := 1;
SetLength(targets[1], 1); targets[1][0] := 1;
SetLength(inputs[2], 2); inputs[2][0] := 1; inputs[2][1] := 0;
SetLength(targets[2], 1); targets[2][0] := 1;
SetLength(inputs[3], 2); inputs[3][0] := 1; inputs[3][1] := 1;
SetLength(targets[3], 1); targets[3][0] := 0;
Writeln('Обучение (5000 эпох, lr = 0.7)...');
errVec := nn.Train(inputs, targets, 5000, 0.7);
Writeln;
Writeln('Итоговая ошибка: ', errVec[errVec.Length - 1]:0:8);
Writeln;
Writeln('Результаты:');
Writeln(' Вход -> Выход сети (ожидание)');
for i := 0 to 3 do
begin
pred := nn.Predict(MakeVec([inputs[i][0], inputs[i][1]]));
Write(' [', inputs[i][0]:1:0, ', ', inputs[i][1]:1:0, '] -> ');
Write(pred[0]:0:6, ' (', targets[i][0]:1:0, ')');
if Abs(pred[0] - targets[i][0]) < 0.5 then
Writeln(' +')
else
Writeln(' -');
end;
end;
procedure DemoSine;
var
nn: TNeuralNetwork;
inputs, targets: array of TRealArray;
i: integer;
x, y_true, y_pred: real;
errVec: TRealArray;
begin
Writeln;
Writeln('-------------------------------------------');
Writeln('ДЕМО 2: sin(x) - аппроксимация функции');
Writeln('Архитектура: 1 -> 16 (tanh) -> 1 (tanh)');
Writeln('-------------------------------------------');
Writeln;
nn := new TNeuralNetwork;
nn.AddLayer(1, 16, actTanh);
nn.AddLayer(16, 1, actTanh);
SetLength(inputs, 200);
SetLength(targets, 200);
for i := 0 to 199 do
begin
x := (i / 199.0) * 2.0 * Pi;
SetLength(inputs[i], 1);
inputs[i][0] := x / (2.0 * Pi);
SetLength(targets[i], 1);
targets[i][0] := Sin(x);
end;
Writeln('Обучение (8000 эпох, lr = 0.3)...');
errVec := nn.Train(inputs, targets, 8000, 0.3);
Writeln;
Writeln('Итоговая ошибка: ', errVec[errVec.Length - 1]:0:8);
Writeln;
Writeln('Выборочные значения:');
Writeln(' x sin(x) Предсказание Ошибка');
for i := 0 to 10 do
begin
x := (i / 10.0) * 2.0 * Pi;
y_pred := nn.Predict(MakeVec([x / (2.0 * Pi)]))[0];
y_true := Sin(x);
Writeln(' ', x:0:3, ' ', y_true:0:6, ' ', y_pred:0:6, ' ', Abs(y_pred - y_true):0:6);
end;
end;
procedure EngineeringCalculator;
var
choice: string;
a, b, res: real;
done: boolean;
begin
done := false;
while not done do
begin
Writeln;
Writeln(' ИНЖЕНЕРНЫЙ КАЛЬКУЛЯТОР');
Writeln;
Writeln(' АРИФМЕТИКА:');
Writeln(' 1. Сложение a + b');
Writeln(' 2. Вычитание a - b');
Writeln(' 3. Умножение a * b');
Writeln(' 4. Деление a / b');
Writeln(' 5. Степень a ^ b');
Writeln(' 6. Корень n-й степени (a)√b');
Writeln;
Writeln(' ТРИГОНОМЕТРИЯ (углы в градусах!):');
Writeln(' 7. sin(x) 8. cos(x)');
Writeln(' 9. tan(x) 10. arcsin(x)');
Writeln(' 11. arccos(x) 12. arctan(x)');
Writeln;
Writeln(' ЛОГАРИФМЫ И СТЕПЕНИ:');
Writeln(' 13. ln(x) 14. lg(x) (log10)');
Writeln(' 15. log(a,b) 16. exp(x)');
Writeln(' 17. sqrt(x) 18. |x| (модуль)');
Writeln;
Writeln(' ДОПОЛНИТЕЛЬНО:');
Writeln(' 19. x! (факториал)');
Writeln(' 20. radians -> degrees');
Writeln(' 21. degrees -> radians');
Writeln(' 22. π (вывести число Pi)');
Writeln(' 23. e (вывести число e)');
Writeln;
Writeln(' 0 — Вернуться в меню');
Writeln;
Write('Выберите операцию: ');
Readln(choice);
case choice of
'0': done := true;
'1':
begin
Write('a = '); Readln(a);
Write('b = '); Readln(b);
res := a + b;
Writeln(a:0:10, ' + ', b:0:10, ' = ', res:0:10);
end;
'2':
begin
Write('a = '); Readln(a);
Write('b = '); Readln(b);
res := a - b;
Writeln(a:0:10, ' - ', b:0:10, ' = ', res:0:10);
end;
'3':
begin
Write('a = '); Readln(a);
Write('b = '); Readln(b);
res := a * b;
Writeln(a:0:10, ' * ', b:0:10, ' = ', res:0:10);
end;
'4':
begin
Write('a = '); Readln(a);
Write('b = '); Readln(b);
if Abs(b) < 1e-15 then
Writeln('Ошибка: деление на ноль!')
else
begin
res := a / b;
Writeln(a:0:10, ' / ', b:0:10, ' = ', res:0:10);
end;
end;
'5':
begin
Write('a (основание) = '); Readln(a);
Write('b (показатель) = '); Readln(b);
if (a < 0) and (Abs(b - Round(b)) > 1e-12) then
Writeln('Ошибка: отрицательное основание в дробной степени')
else
begin
res := Power(a, b);
Writeln(a:0:6, ' ^ ', b:0:6, ' = ', res:0:10);
end;
end;
'6':
begin
Write('a (степень корня) = '); Readln(a);
Write('b (подкоренное) = '); Readln(b);
if (a <= 0) or (Abs(a - Round(a)) > 1e-12) then
Writeln('Ошибка: степень корня должна быть целым положительным числом')
else if (b < 0) and (Round(a) mod 2 = 0) then
Writeln('Ошибка: корень чётной степени из отрицательного числа')
else
begin
if b < 0 then
res := -Power(-b, 1.0 / a)
else
res := Power(b, 1.0 / a);
Writeln('root(', Round(a):1, ', ', b:0:6, ') = ', res:0:10);
end;
end;
'7':
begin
Write('x (градусы) = '); Readln(a);
res := Sin(a * Pi / 180.0);
Writeln('sin(', a:0:6, ' deg) = ', res:0:10);
end;
'8':
begin
Write('x (градусы) = '); Readln(a);
res := Cos(a * Pi / 180.0);
Writeln('cos(', a:0:6, ' deg) = ', res:0:10);
end;
'9':
begin
Write('x (градусы) = '); Readln(a);
if Abs(Cos(a * Pi / 180.0)) < 1e-12 then
Writeln('Ошибка: тангенс не определён для этого угла')
else
begin
res := Tan(a * Pi / 180.0);
Writeln('tan(', a:0:6, ' deg) = ', res:0:10);
end;
end;
'10':
begin
Write('x = '); Readln(a);
if (a < -1.0) or (a > 1.0) then
Writeln('Ошибка: аргумент должен быть в [-1, 1]')
else
begin
res := ArcSin(a) * 180.0 / Pi;
Writeln('arcsin(', a:0:6, ') = ', res:0:6, ' deg');
end;
end;
'11':
begin
Write('x = '); Readln(a);
if (a < -1.0) or (a > 1.0) then
Writeln('Ошибка: аргумент должен быть в [-1, 1]')
else
begin
res := ArcCos(a) * 180.0 / Pi;
Writeln('arccos(', a:0:6, ') = ', res:0:6, ' deg');
end;
end;
'12':
begin
Write('x = '); Readln(a);
res := ArcTan(a) * 180.0 / Pi;
Writeln('arctan(', a:0:6, ') = ', res:0:6, ' deg');
end;
'13':
begin
Write('x = '); Readln(a);
if a <= 0 then
Writeln('Ошибка: логарифм от неположительного числа')
else
begin
res := Ln(a);
Writeln('ln(', a:0:6, ') = ', res:0:10);
end;
end;
'14':
begin
Write('x = '); Readln(a);
if a <= 0 then
Writeln('Ошибка: логарифм от неположительного числа')
else
begin
res := Log10(a);
Writeln('lg(', a:0:6, ') = ', res:0:10);
end;
end;
'15':
begin
Write('a (основание логарифма) = '); Readln(a);
Write('b (аргумент) = '); Readln(b);
if (a <= 0) or (a = 1) or (b <= 0) then
Writeln('Ошибка: неверные аргументы (a>0, a<>1, b>0)')
else
begin
res := Ln(b) / Ln(a);
Writeln('log_', a:0:3, '(', b:0:6, ') = ', res:0:10);
end;
end;
'16':
begin
Write('x = '); Readln(a);
res := Exp(a);
Writeln('exp(', a:0:6, ') = ', res:0:10);
end;
'17':
begin
Write('x = '); Readln(a);
if a < 0 then
Writeln('Ошибка: корень из отрицательного числа')
else
begin
res := Sqrt(a);
Writeln('sqrt(', a:0:6, ') = ', res:0:10);
end;
end;
'18':
begin
Write('x = '); Readln(a);
res := Abs(a);
Writeln('|', a:0:6, '| = ', res:0:10);
end;
'19':
begin
Write('x = '); Readln(a);
if (a < 0) or (a > 170) then
Writeln('Ошибка: факториал для x<0 или x>170 (переполнение)')
else
begin
res := 1.0;
for var k := 2 to Round(a) do
res := res * k;
Writeln(Round(a), '! = ', res:0:0);
end;
end;
'20':
begin
Write('radians = '); Readln(a);
res := a * 180.0 / Pi;
Writeln(a:0:6, ' rad = ', res:0:6, ' deg');
end;
'21':
begin
Write('degrees = '); Readln(a);
res := a * Pi / 180.0;
Writeln(a:0:6, ' deg = ', res:0:6, ' rad');
end;
'22':
begin
Writeln('pi = ', Pi:0:15);
end;
'23':
begin
Writeln('e = ', Exp(1.0):0:15);
end;
else
Writeln('Неверный выбор. Попробуйте снова.');
end;
if choice <> '0' then
begin
Writeln;
Write('Нажмите Enter для продолжения...');
Readln;
end;
end;
end;
var
cmd: string;
begin
Console.OutputEncoding := Encoding.UTF8;
while true do
begin
Writeln('======================================');
Writeln(' Нейросеть на PascalABC.NET');
Writeln(' Полносвязный перцептрон');
Writeln('======================================');
Writeln;
Writeln('Выберите раздел:');
Writeln(' 1 - XOR (исключающее ИЛИ)');
Writeln(' 2 - sin(x) (аппроксимация функции)');
Writeln(' 3 - Обе демонстрации');
Writeln(' 4 - Инженерный калькулятор');
Writeln(' q - Выход');
Writeln;
Write('Ваш выбор: ');
Readln(cmd);
case cmd of
'1': DemoXOR;
'2': DemoSine;
'3':
begin
DemoXOR;
DemoSine;
end;
'4': EngineeringCalculator;
'q': Break;
else
begin
Writeln('Неверный выбор. Попробуйте снова.');
Writeln;
Write('Нажмите Enter...');
Readln;
end;
end;
end;
end.

Показать полностью
1

Делаю домашку...

Нам задали создать такую деталь на программе "КОМПАС".

По какой-то причине не могу загрузить ссылку на видео в рутубе...

Показать полностью
Отличная работа, все прочитано!

Темы

Политика

Теги

Популярные авторы

Сообщества

18+

Теги

Популярные авторы

Сообщества

Игры

Теги

Популярные авторы

Сообщества

Юмор

Теги

Популярные авторы

Сообщества

Отношения

Теги

Популярные авторы

Сообщества

Здоровье

Теги

Популярные авторы

Сообщества

Путешествия

Теги

Популярные авторы

Сообщества

Спорт

Теги

Популярные авторы

Сообщества

Хобби

Теги

Популярные авторы

Сообщества

Сервис

Теги

Популярные авторы

Сообщества

Природа

Теги

Популярные авторы

Сообщества

Бизнес

Теги

Популярные авторы

Сообщества

Транспорт

Теги

Популярные авторы

Сообщества

Общение

Теги

Популярные авторы

Сообщества

Юриспруденция

Теги

Популярные авторы

Сообщества

Наука

Теги

Популярные авторы

Сообщества

IT

Теги

Популярные авторы

Сообщества

Животные

Теги

Популярные авторы

Сообщества

Кино и сериалы

Теги

Популярные авторы

Сообщества

Экономика

Теги

Популярные авторы

Сообщества

Кулинария

Теги

Популярные авторы

Сообщества

История

Теги

Популярные авторы

Сообщества

Недвижимость и ремонт

Теги

Популярные авторы

Сообщества