Идея фикс "Сделать свой Stable Diffusion на Паскале"

Любые обсуждения, не нарушающие правил форума.

Модератор: Модераторы

Ответить
Alex2013
долгожитель
Сообщения: 3282
Зарегистрирован: 03.04.2013 11:59:44

Идея фикс "Сделать свой Stable Diffusion на Паскале"

Сообщение Alex2013 »

Пока не проект а просто тема для обсуждения
По следам публикации на трубе
Сделал свой Stable Diffusion с нуля на Tensorflow. Это проще, чем кажется.
Основная мысль просто более подробно разобраться в требухе Stable Diffusion и генеративных нейросетей вообще.

Возможный профит в возможности использования упрощенных целевых моделей в своих проектах
А так-же проверке некоторых интересных перспективных идей вроде этой

Как я собрал «Термодинамический Мозг» с фазой сна и митозом, который влезет в Arduino

и этой
Генератор карт трассировки для альтернативы сверточным нейронным сетям.(исходники кстати на FPC )

В общем как думаете это в принципе возможно или "полная маниловщина" ? :roll:
Зы
Разумеется "LLM на Паскале" была бы полезнее и интереснее но у меня нет даже приблизительного понимания с чего там можно начать и отчего оттолкнуться

Хотя пример как раз есть! ( Старый безумец BeRo1985 снова в деле! )
https://github.com/BeRo1985/pasllm?yscl ... i792073156
Alex2013
долгожитель
Сообщения: 3282
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

Сделал свой Stable Diffusion с нуля на Tensorflow. Это проще, чем кажется.
(Пересказ и "оглавление ролика")
00:00 Введение в проблему

• Изображение состоит из пикселей, каждый из которых имеет три цвета: красный, синий и зелёный.
• Для изображения размером 64x64 количество параметров огромно.
• Количество осмысленных изображений значительно меньше.

00:59 Многомерное пространство и диффузионные модели

• Каждый параметр изображения представляет собой ось в многомерном пространстве.
• Это пространство чрезвычайно разреженное, и случайная точка в нём часто приводит к шуму.
• Диффузионные модели хорошо работают с такими пространствами.

01:30 Принцип работы диффузионных моделей

• Диффузионные модели используют процесс прямой диффузии для предсказания предыдущих шагов.
• Обученная нейросеть позволяет обратить процесс диффузии вспять и получить осмысленное изображение из шума.

02:11 Реализация генератора изображений

• Адаптация системы обратной диффузии для многомерного пространства.
• Использование набора данных изображений мордочек котиков.

02:59 Синусоидальное тайминг-эмбеддирование

• Синусоидальное тайминг-эмбеддирование преобразует время диффузии в 32 числа.
• Визуализация работы функции синусоидального тайминг-эмбеддирования.

05:45 Сборка нейросети

• Настройка количества фильтров и вычислительных блоков в нейросети.
• Применение временного эмбеддирования в понижающих блоках.

07:44 Нормализация изображений

• Добавление нормализатора для адаптации параметров изображений.
• Адаптация нормализатора под данные из датасета.

08:52 Типы диффузионных моделей

• Явная диффузия: определение количества шума в изображении и его удаление.
• Неявная диффузия: предсказание всего вложенного шума и возможность регулировки количества шагов.

10:37 Обучение нейросети и генерация изображений

• Обучение нейросети на нескольких эпохах.
• Регулировка количества шагов для улучшения качества генерации.

12:18 Эксперименты с другими наборами данных

• Использование набора данных скошенной травы для обучения генератора картинок.
• Настройка размера картинки и режима интерполяции.
• Эксперименты с разными типами сверточных блоков.

13:19 Адаптация нормализатора и обучение нейросети

• Не забываем адаптировать нормализатор.
• Обучили нейросеть за 50 эпох.
• Результат лучше, чем у генеративно-состязательных нейросетей на том же датасете.
• Нейросеть обобщила 900 изображений, создавая текстуры травы, листики, дорогу и отражения.

14:22 Направленная диффузия

• Нейросети стремятся сократить ошибку, используя предоставленную информацию.
• Можно подать нейросети кусочек картинки или текстовое описание для создания направленной диффузии.
• Это позволит нейросети опираться на предоставленную информацию при генерации изображения.
• Обсуждение направленной диффузии будет в следующем видео.
Аватара пользователя
Alexander
энтузиаст
Сообщения: 898
Зарегистрирован: 18.12.2005 18:10:00
Откуда: оттуда
Контактная информация:

Сообщение Alexander »

Какие интересные ссылки. То есть и PasLLM уже написали для запуска готовых моделей.

Думаю для тренировки можно и свой Stable Diffusion сделать. Возможно и лучше оригинального получится.
"LLM на Паскале" была бы полезнее
Ну да чатик с ИИ сделать с работой на CPU. PasLLM уже есть готовый. Я покрутил так: http://soft.self-made-free.ru/II5EXT_107.tar.xz там наброски WordEmbedding, трансформера с контекстом с обучением, самые предварительные наброски рассуждающего ИИ. Что нужно? Кодер NLP, трансформер, декодер NLP для нейросетевого генеративного ИИ. А в рассуждающем всё по другому, но и эта тема там чуть-чуть зацеплена.

Для Stable Diffusion (да и для чата и прочего) возможно будут полезны юниты для работы с простым PPM форматом.

Код: Выделить всё

unit PPMReader;

{
    PPM file reader.
    For GNU/Linux 64 bit version.
    Version: 1.
    Written on FreePascal (https://freepascal.org/).
    Copyright (C) 2025-2026 Artyomov Alexander
    http://self-made-free.ru/
    aralni@mail.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
}

{$MODE OBJFPC}{$H+}
{$MODESWITCH ADVANCEDRECORDS}

interface

uses
  SysUtils, Classes, BaseUnix, Unix;

type
  TNetPBMType = (PBM_P1, PBM_P4, PGM_P2, PGM_P5, PPM_P3, PPM_P6, PPM_Unknown);

  TNetPBMImage = record
    Width, Height: Integer;
    MaxValue: Integer;
    ImageType: TNetPBMType;
    Data: PByte;
    Size: Int64;
    FileName: String;
    IsMapped: Boolean;
    FFileHandle: cint;
    DataStart: PByte;  // Начало данных изображения
    HeaderSize: Int64; // Размер заголовка
  end;

  TRGBPixel = packed record
    R, G, B: Byte;
  end;
  PRGBPixel = ^TRGBPixel;

  TRGBImage = array of array of TRGBPixel;
  TGrayscaleImage = array of array of Byte;
  TBinaryImage = array of array of Boolean;

function LoadNetPBM(var Image: TNetPBMImage; const FileName: string): Boolean;
procedure FreeNetPBM(var Image: TNetPBMImage);
function NetPBMToRGBMatrix(const Image: TNetPBMImage): TRGBImage;
function NetPBMToGrayscaleMatrix(const Image: TNetPBMImage): TGrayscaleImage;
function NetPBMToBinaryMatrix(const Image: TNetPBMImage): TBinaryImage;
function GetPixel(const Image: TNetPBMImage; X, Y: Integer): TRGBPixel;
function GetGrayscalePixel(const Image: TNetPBMImage; X, Y: Integer): Byte;
function GetBinaryPixel(const Image: TNetPBMImage; X, Y: Integer): Boolean;
function IsValidNetPBM(const Image: TNetPBMImage): Boolean;
procedure PrintNetPBMInfo(const Image: TNetPBMImage);
function GetImageTypeName(ImageType: TNetPBMType): string;

implementation

function GetImageTypeName(ImageType: TNetPBMType): string;
begin
  case ImageType of
    PBM_P1: Result := 'P1 (ASCII PBM)';
    PBM_P4: Result := 'P4 (Binary PBM)';
    PGM_P2: Result := 'P2 (ASCII PGM)';
    PGM_P5: Result := 'P5 (Binary PGM)';
    PPM_P3: Result := 'P3 (ASCII PPM)';
    PPM_P6: Result := 'P6 (Binary PPM)';
    else Result := 'Unknown';
  end;
end;

function SkipCommentsAndWhitespace(var Ptr: PByte; var BytesRead: Int64; MaxSize: Int64): Boolean;
var
  C: Char;
  InComment: Boolean;
begin
  InComment := False;
  Result := False;

  while BytesRead < MaxSize do
  begin
    C := Char(Ptr^);

    if C = '#' then
      InComment := True
    else if C = #10 then
      InComment := False
    else if not InComment and (C in ['0'..'9', 'P']) then
    begin
      Result := True;
      Exit;
    end
    else if not InComment and not (C in [#9, #10, #13, #32]) then
    begin
      // Неожиданный символ
      Exit;
    end;

    Inc(Ptr);
    Inc(BytesRead);
  end;
end;

function ReadNumber(var Ptr: PByte; var BytesRead: Int64; MaxSize: Int64): Integer;
var
  C: Char;
  NumStr: string;
begin
  Result := 0;
  NumStr := '';

  while BytesRead < MaxSize do
  begin
    C := Char(Ptr^);

    if C in ['0'..'9'] then
    begin
      NumStr := NumStr + C;
      Inc(Ptr);
      Inc(BytesRead);
    end
    else if C in [#9, #10, #13, #32] then
    begin
      if NumStr <> '' then
      begin
        Result := StrToIntDef(NumStr, 0);
        Exit;
      end;
      Inc(Ptr);
      Inc(BytesRead);
    end
    else
    begin
      if NumStr <> '' then
        Result := StrToIntDef(NumStr, 0);
      Exit;
    end;
  end;

  if NumStr <> '' then
    Result := StrToIntDef(NumStr, 0);
end;

function CalculateBinaryPBMDataSize(Width, Height: Integer): Int64;
begin
  // PBM binary: каждый ряд выровнен до границы байта
  Result := Height * ((Width + 7) div 8);
end;

function LoadNetPBM(var Image: TNetPBMImage; const FileName: string): Boolean;
var
  OriginalData: PByte;
  Ptr: PByte;
  BytesRead: Int64;
begin
  Result := False;
  FillChar(Image, SizeOf(Image), 0);
  Image.FileName := FileName;
  Image.IsMapped := False;

  // Открываем файл
  Image.FFileHandle := FpOpen(FileName, O_RDONLY or O_LARGEFILE);
  if Image.FFileHandle = -1 then
  begin
    WriteLn('Error: Cannot open file ', FileName);
    Exit;
  end;

  try
    // Получаем размер файла
    Image.Size := FpLSeek(Image.FFileHandle, 0, SEEK_END);
    FpLSeek(Image.FFileHandle, 0, SEEK_SET);

    if Image.Size < 10 then
    begin
      WriteLn('Error: File too small to be a valid NetPBM');
      Exit;
    end;

    // Memory map файл
    OriginalData := PByte(FpMMap(nil, Image.Size, PROT_READ, MAP_SHARED, Image.FFileHandle, 0));
    if OriginalData = Pointer(-1) then
    begin
      WriteLn('Error: MMap failed for file ', FileName);
      Exit;
    end;

    Image.IsMapped := True;
    Image.Data := OriginalData;

    // Парсим заголовок NetPBM
    Ptr := OriginalData;
    BytesRead := 0;

    // Читаем magic number
    if not SkipCommentsAndWhitespace(Ptr, BytesRead, Image.Size) then
    begin
      WriteLn('Error: Cannot find NetPBM magic number');
      Exit;
    end;

    // Читаем 'P'
    if Char(Ptr^) <> 'P' then
    begin
      WriteLn('Error: Not a NetPBM file (missing P)');
      Exit;
    end;

    Inc(Ptr);
    Inc(BytesRead);

    // Читаем тип
    if BytesRead >= Image.Size then
    begin
      WriteLn('Error: Unexpected end of file after P');
      Exit;
    end;

    case Char(Ptr^) of
      '1': Image.ImageType := PBM_P1;
      '4': Image.ImageType := PBM_P4;
      '2': Image.ImageType := PGM_P2;
      '5': Image.ImageType := PGM_P5;
      '3': Image.ImageType := PPM_P3;
      '6': Image.ImageType := PPM_P6;
    else
      WriteLn('Error: Unknown NetPBM type: P', Char(Ptr^));
      Image.ImageType := PPM_Unknown;
      Exit;
    end;

    Inc(Ptr);
    Inc(BytesRead);

    // Пропускаем до ширины
    if not SkipCommentsAndWhitespace(Ptr, BytesRead, Image.Size) then
    begin
      WriteLn('Error: Cannot find width');
      Exit;
    end;

    // Читаем ширину
    Image.Width := ReadNumber(Ptr, BytesRead, Image.Size);
    if Image.Width <= 0 then
    begin
      WriteLn('Error: Invalid width: ', Image.Width);
      Exit;
    end;

    // Пропускаем до высоты
    if not SkipCommentsAndWhitespace(Ptr, BytesRead, Image.Size) then
    begin
      WriteLn('Error: Cannot find height');
      Exit;
    end;

    // Читаем высоту
    Image.Height := ReadNumber(Ptr, BytesRead, Image.Size);
    if Image.Height <= 0 then
    begin
      WriteLn('Error: Invalid height: ', Image.Height);
      Exit;
    end;

    // Для PBM нет MaxValue, для PGM/PPM есть
    if Image.ImageType in [PGM_P2, PGM_P5, PPM_P3, PPM_P6] then
    begin
      // Пропускаем до максимального значения
      if not SkipCommentsAndWhitespace(Ptr, BytesRead, Image.Size) then
      begin
        WriteLn('Error: Cannot find max value');
        Exit;
      end;

      // Читаем максимальное значение
      Image.MaxValue := ReadNumber(Ptr, BytesRead, Image.Size);
      if Image.MaxValue <= 0 then
      begin
        WriteLn('Error: Invalid max value: ', Image.MaxValue);
        Exit;
      end;
    end
    else
    begin
      // PBM всегда имеет MaxValue = 1
      Image.MaxValue := 1;
    end;

    // Пропускаем один пробельный символ после заголовка
    if (BytesRead < Image.Size) and (Char(Ptr^) in [#9, #10, #13, #32]) then
    begin
      Inc(Ptr);
      Inc(BytesRead);
    end;

    // Сохраняем начало данных и размер заголовка
    Image.DataStart := Ptr;
    Image.HeaderSize := BytesRead;

    // Проверяем размер данных
    case Image.ImageType of
      PPM_P6: 
        if (Image.Size - BytesRead) < (Image.Width * Image.Height * 3) then
        begin
          WriteLn('Error: File too small for PPM image data');
          Exit;
        end;

      PGM_P5:
        if (Image.Size - BytesRead) < (Image.Width * Image.Height) then
        begin
          WriteLn('Error: File too small for PGM image data');
          Exit;
        end;

      PBM_P4:
        if (Image.Size - BytesRead) < CalculateBinaryPBMDataSize(Image.Width, Image.Height) then
        begin
          WriteLn('Error: File too small for PBM image data');
          Exit;
        end;
    end;

    Result := True;
    WriteLn(Format('Loaded NetPBM: %s, Type: %s, Size: %dx%d, MaxValue: %d', 
      [FileName, GetImageTypeName(Image.ImageType), Image.Width, Image.Height, Image.MaxValue]));

  except
    on E: Exception do
    begin
      WriteLn('Error loading NetPBM: ', E.Message);
      FreeNetPBM(Image);
    end;
  end;
end;

procedure FreeNetPBM(var Image: TNetPBMImage);
begin
  if Image.IsMapped and (Image.Data <> nil) then
  begin
    FpMUnMap(Image.Data, Image.Size);
    if Image.FFileHandle <> -1 then
      FpClose(Image.FFileHandle);
  end;
  FillChar(Image, SizeOf(Image), 0);
end;

function NetPBMToRGBMatrix(const Image: TNetPBMImage): TRGBImage;
var
  X, Y: Integer;
  Ptr: PByte;
  Scale: Double;
  GrayValue: Byte;
begin
  if not IsValidNetPBM(Image) then
    Exit(nil);

  SetLength(Result, Image.Height, Image.Width);

  case Image.ImageType of
    PPM_P6: 
    begin
      Ptr := Image.DataStart;
      Scale := 255.0 / Image.MaxValue;

      for Y := 0 to Image.Height - 1 do
        for X := 0 to Image.Width - 1 do
        begin
          Result[Y, X].R := Byte(Round(Ptr^ * Scale));
          Inc(Ptr);
          Result[Y, X].G := Byte(Round(Ptr^ * Scale));
          Inc(Ptr);
          Result[Y, X].B := Byte(Round(Ptr^ * Scale));
          Inc(Ptr);
        end;
    end;

    PGM_P5:
    begin
      Ptr := Image.DataStart;
      Scale := 255.0 / Image.MaxValue;

      for Y := 0 to Image.Height - 1 do
        for X := 0 to Image.Width - 1 do
        begin
          GrayValue := Byte(Round(Ptr^ * Scale));
          Result[Y, X].R := GrayValue;
          Result[Y, X].G := GrayValue;
          Result[Y, X].B := GrayValue;
          Inc(Ptr);
        end;
    end;

    PBM_P4:
    begin
      // PBM: 0 = белый, 1 = черный (в бинарном формате)
      for Y := 0 to Image.Height - 1 do
        for X := 0 to Image.Width - 1 do
        begin
          if GetBinaryPixel(Image, X, Y) then
          begin
            Result[Y, X].R := 0;   // Черный
            Result[Y, X].G := 0;
            Result[Y, X].B := 0;
          end
          else
          begin
            Result[Y, X].R := 255; // Белый
            Result[Y, X].G := 255;
            Result[Y, X].B := 255;
          end;
        end;
    end;

  else
    WriteLn('Warning: Only P6, P5, P4 formats fully supported for RGB conversion');
    // Заполняем серым цветом
    for Y := 0 to Image.Height - 1 do
      for X := 0 to Image.Width - 1 do
      begin
        Result[Y, X].R := 128;
        Result[Y, X].G := 128;
        Result[Y, X].B := 128;
      end;
  end;
end;

function NetPBMToGrayscaleMatrix(const Image: TNetPBMImage): TGrayscaleImage;
var
  X, Y: Integer;
  Ptr: PByte;
  Scale: Double;
begin
  if not IsValidNetPBM(Image) then
    Exit(nil);

  SetLength(Result, Image.Height, Image.Width);

  case Image.ImageType of
    PPM_P6: 
    begin
      Ptr := Image.DataStart;
      Scale := 255.0 / Image.MaxValue;

      for Y := 0 to Image.Height - 1 do
        for X := 0 to Image.Width - 1 do
        begin
          // Конвертируем RGB в grayscale
          Result[Y, X] := Byte(Round(
            (0.299 * (Ptr^ * Scale) + 
             0.587 * ((Ptr + 1)^ * Scale) + 
             0.114 * ((Ptr + 2)^ * Scale))));
          Inc(Ptr, 3);
        end;
    end;

    PGM_P5:
    begin
      Ptr := Image.DataStart;
      Scale := 255.0 / Image.MaxValue;

      for Y := 0 to Image.Height - 1 do
        for X := 0 to Image.Width - 1 do
        begin
          Result[Y, X] := Byte(Round(Ptr^ * Scale));
          Inc(Ptr);
        end;
    end;

    PBM_P4:
    begin
      for Y := 0 to Image.Height - 1 do
        for X := 0 to Image.Width - 1 do
        begin
          if GetBinaryPixel(Image, X, Y) then
            Result[Y, X] := 0   // Черный
          else
            Result[Y, X] := 255; // Белый
        end;
    end;
  end;
end;

function NetPBMToBinaryMatrix(const Image: TNetPBMImage): TBinaryImage;
var
  X, Y: Integer;
  Ptr: PByte;
  Offset: Int64;
  BitIndex: Integer;
  ByteVal: Byte;
  Threshold: Byte;
begin
  if not IsValidNetPBM(Image) then
    Exit(nil);

  SetLength(Result, Image.Height, Image.Width);

  case Image.ImageType of
    PBM_P4:
    begin
      // PBM binary format: биты упакованы, 0=белый, 1=черный
      for Y := 0 to Image.Height - 1 do
      begin
        for X := 0 to Image.Width - 1 do
        begin
          Offset := Y * ((Image.Width + 7) div 8) + (X div 8);
          Ptr := Image.DataStart + Offset;
          BitIndex := 7 - (X mod 8); // Старший бит первый

          if Ptr < (Image.Data + Image.Size) then
          begin
            ByteVal := Ptr^;
            Result[Y, X] := (ByteVal and (1 shl BitIndex)) <> 0;
          end
          else
          begin
            Result[Y, X] := False;
          end;
        end;
      end;
    end;

    PGM_P5, PPM_P6:
    begin
      // Для grayscale и color изображений используем порог 128
      Threshold := 128;
      for Y := 0 to Image.Height - 1 do
        for X := 0 to Image.Width - 1 do
        begin
          Result[Y, X] := GetGrayscalePixel(Image, X, Y) < Threshold;
        end;
    end;

  else
    // Для других форматов заполняем False
    for Y := 0 to Image.Height - 1 do
      for X := 0 to Image.Width - 1 do
        Result[Y, X] := False;
  end;
end;

function GetPixel(const Image: TNetPBMImage; X, Y: Integer): TRGBPixel;
var
  Ptr: PByte;
  Offset: Int64;
  Scale: Double;
  GrayValue: Byte;
  BitIndex: Integer;
  ByteVal: Byte;
begin
  FillChar(Result, SizeOf(Result), 0);

  if not IsValidNetPBM(Image) or (X < 0) or (X >= Image.Width) or (Y < 0) or (Y >= Image.Height) then
    Exit;

  case Image.ImageType of
    PPM_P6:
    begin
      Scale := 255.0 / Image.MaxValue;
      Offset := (Y * Image.Width + X) * 3;
      Ptr := Image.DataStart + Offset;

      if (Ptr + 2) < (Image.Data + Image.Size) then
      begin
        Result.R := Byte(Round(Ptr^ * Scale));
        Result.G := Byte(Round((Ptr + 1)^ * Scale));
        Result.B := Byte(Round((Ptr + 2)^ * Scale));
      end;
    end;

    PGM_P5:
    begin
      Scale := 255.0 / Image.MaxValue;
      Offset := Y * Image.Width + X;
      Ptr := Image.DataStart + Offset;

      if Ptr < (Image.Data + Image.Size) then
      begin
        GrayValue := Byte(Round(Ptr^ * Scale));
        Result.R := GrayValue;
        Result.G := GrayValue;
        Result.B := GrayValue;
      end;
    end;

    PBM_P4:
    begin
      // PBM binary format: биты упакованы, 0=белый, 1=черный
      Offset := Y * ((Image.Width + 7) div 8) + (X div 8);
      Ptr := Image.DataStart + Offset;
      BitIndex := 7 - (X mod 8); // Старший бит первый

      if Ptr < (Image.Data + Image.Size) then
      begin
        ByteVal := Ptr^;
        if (ByteVal and (1 shl BitIndex)) <> 0 then
        begin
          Result.R := 0;   // Черный
          Result.G := 0;
          Result.B := 0;
        end
        else
        begin
          Result.R := 255; // Белый
          Result.G := 255;
          Result.B := 255;
        end;
      end;
    end;
  end;
end;

function GetGrayscalePixel(const Image: TNetPBMImage; X, Y: Integer): Byte;
var
  Pixel: TRGBPixel;
begin
  Pixel := GetPixel(Image, X, Y);
  Result := Byte(Round(0.299 * Pixel.R + 0.587 * Pixel.G + 0.114 * Pixel.B));
end;

function GetBinaryPixel(const Image: TNetPBMImage; X, Y: Integer): Boolean;
var
  Ptr: PByte;
  Offset: Int64;
  BitIndex: Integer;
  ByteVal: Byte;
begin
  Result := False;

  if not IsValidNetPBM(Image) or (X < 0) or (X >= Image.Width) or (Y < 0) or (Y >= Image.Height) then
    Exit;

  if Image.ImageType = PBM_P4 then
  begin
    Offset := Y * ((Image.Width + 7) div 8) + (X div 8);
    Ptr := Image.DataStart + Offset;
    BitIndex := 7 - (X mod 8);

    if Ptr < (Image.Data + Image.Size) then
    begin
      ByteVal := Ptr^;
      Result := (ByteVal and (1 shl BitIndex)) <> 0;
    end;
  end
  else
  begin
    // Для других форматов считаем черным если значение < 128
    Result := GetGrayscalePixel(Image, X, Y) < 128;
  end;
end;

function IsValidNetPBM(const Image: TNetPBMImage): Boolean;
begin
  Result := (Image.IsMapped) and (Image.Data <> nil) and 
            (Image.ImageType <> PPM_Unknown) and 
            (Image.Width > 0) and (Image.Height > 0) and
            (Image.MaxValue > 0) and (Image.MaxValue <= 65535) and
            (Image.DataStart <> nil) and (Image.HeaderSize > 0);
end;

procedure PrintNetPBMInfo(const Image: TNetPBMImage);
var
  ExpectedDataSize: Int64;
  ActualDataSize: Int64;
begin
  if not Image.IsMapped then
  begin
    WriteLn('NetPBM image not loaded');
    Exit;
  end;

  WriteLn('NetPBM File: ', Image.FileName);
  WriteLn('Type: ', GetImageTypeName(Image.ImageType));
  WriteLn('Size: ', Image.Width, ' x ', Image.Height);
  WriteLn('Max Value: ', Image.MaxValue);
  WriteLn('Total File Size: ', Image.Size, ' bytes');
  WriteLn('Header Size: ', Image.HeaderSize, ' bytes');

  case Image.ImageType of
    PPM_P6: ExpectedDataSize := Image.Width * Image.Height * 3;
    PGM_P5: ExpectedDataSize := Image.Width * Image.Height;
    PBM_P4: ExpectedDataSize := CalculateBinaryPBMDataSize(Image.Width, Image.Height);
  else
    ExpectedDataSize := 0;
  end;

  if ExpectedDataSize > 0 then
  begin
    ActualDataSize := Image.Size - Image.HeaderSize;
    WriteLn('Expected Data Size: ', ExpectedDataSize, ' bytes');
    WriteLn('Actual Data Size: ', ActualDataSize, ' bytes');
    WriteLn('Data Size Match: ', ExpectedDataSize = ActualDataSize);
  end;

  WriteLn('Mapped: ', Image.IsMapped);
  WriteLn('Valid: ', IsValidNetPBM(Image));
end;

end.

Код: Выделить всё

unit PPMWriter;

{
    PPM file writer.
    For GNU/Linux 64 bit version.
    Version: 1.
    Written on FreePascal (https://freepascal.org/).
    Copyright (C) 2025-2026 Artyomov Alexander
    http://self-made-free.ru/
    aralni@mail.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
}

{$MODE OBJFPC}{$H+}
{$MODESWITCH ADVANCEDRECORDS}
{$MODESWITCH UNICODESTRINGS}

interface

uses
  SysUtils, Classes, Math, PPMReader, MatrixOps, DataUtils;

type
  TPPMFormat = (pfPBM, pfPGM, pfPPM, pfAuto);
  TBinaryFormat = (bfASCII, bfBinary);
  
  TPPMWriter = record
    class function SavePBM(const Image: TBinaryImage; const FileName: string; 
                          BinaryFormat: TBinaryFormat = bfBinary): Boolean; static;
    class function SavePGM(const Image: TGrayscaleImage; const FileName: string; 
                          MaxValue: Integer = 255; 
                          BinaryFormat: TBinaryFormat = bfBinary): Boolean; static;
    class function SavePPM(const Image: TRGBImage; const FileName: string; 
                          MaxValue: Integer = 255; 
                          BinaryFormat: TBinaryFormat = bfBinary): Boolean; static;
    
    // Универсальные функции
    class function SaveImage(const Image: TRGBImage; const FileName: string; 
                            Format: TPPMFormat = pfAuto; 
                            MaxValue: Integer = 255): Boolean; static;
    class function SaveMatrixAsPGM(const Matrix: TDoubleMatrix; 
                                  const FileName: string; 
                                  Normalize: Boolean = True; 
                                  MaxValue: Integer = 255): Boolean; static;
    
    // Вспомогательные функции
    class function ConvertToPBMFormat(const Image: TBinaryImage): string; static;
    class function ConvertToPGMFormat(const Image: TGrayscaleImage; 
                                     MaxValue: Integer): string; static;
    class function ConvertToPPMFormat(const Image: TRGBImage; 
                                     MaxValue: Integer): string; static;
  end;

  TNetPBMSaver = class
  private
    class procedure WritePBMHeader(Stream: TStream; Width, Height: Integer; 
                                  Binary: Boolean); static;
    class procedure WritePGMHeader(Stream: TStream; Width, Height, MaxValue: Integer; 
                                  Binary: Boolean); static;
    class procedure WritePPMHeader(Stream: TStream; Width, Height, MaxValue: Integer; 
                                  Binary: Boolean); static;
  public
    class function SaveBinaryPBM(const Image: TBinaryImage; Stream: TStream): Boolean; static;
    class function SaveAsciiPBM(const Image: TBinaryImage; Stream: TStream): Boolean; static;
    class function SaveBinaryPGM(const Image: TGrayscaleImage; Stream: TStream; 
                                MaxValue: Integer): Boolean; static;
    class function SaveAsciiPGM(const Image: TGrayscaleImage; Stream: TStream; 
                               MaxValue: Integer): Boolean; static;
    class function SaveBinaryPPM(const Image: TRGBImage; Stream: TStream; 
                                MaxValue: Integer): Boolean; static;
    class function SaveAsciiPPM(const Image: TRGBImage; Stream: TStream; 
                               MaxValue: Integer): Boolean; static;
  end;

implementation

{ TPPMWriter }

class function TPPMWriter.SavePBM(const Image: TBinaryImage; const FileName: string; 
                                 BinaryFormat: TBinaryFormat): Boolean;
var
  Stream: TFileStream;
begin
  Result := False;
  try
    Stream := TFileStream.Create(FileName, fmCreate);
    try
      if BinaryFormat = bfBinary then
        Result := TNetPBMSaver.SaveBinaryPBM(Image, Stream)
      else
        Result := TNetPBMSaver.SaveAsciiPBM(Image, Stream);
    finally
      Stream.Free;
    end;
  except
    on E: Exception do
      WriteLn('Error saving PBM: ', E.Message);
  end;
end;

class function TPPMWriter.SavePGM(const Image: TGrayscaleImage; const FileName: string; 
                                  MaxValue: Integer; BinaryFormat: TBinaryFormat): Boolean;
var
  Stream: TFileStream;
begin
  Result := False;
  try
    Stream := TFileStream.Create(FileName, fmCreate);
    try
      if BinaryFormat = bfBinary then
        Result := TNetPBMSaver.SaveBinaryPGM(Image, Stream, MaxValue)
      else
        Result := TNetPBMSaver.SaveAsciiPGM(Image, Stream, MaxValue);
    finally
      Stream.Free;
    end;
  except
    on E: Exception do
      WriteLn('Error saving PGM: ', E.Message);
  end;
end;

class function TPPMWriter.SavePPM(const Image: TRGBImage; const FileName: string; 
                                  MaxValue: Integer; BinaryFormat: TBinaryFormat): Boolean;
var
  Stream: TFileStream;
begin
  Result := False;
  try
    Stream := TFileStream.Create(FileName, fmCreate);
    try
      if BinaryFormat = bfBinary then
        Result := TNetPBMSaver.SaveBinaryPPM(Image, Stream, MaxValue)
      else
        Result := TNetPBMSaver.SaveAsciiPPM(Image, Stream, MaxValue);
    finally
      Stream.Free;
    end;
  except
    on E: Exception do
      WriteLn('Error saving PPM: ', E.Message);
  end;
end;

class function TPPMWriter.SaveImage(const Image: TRGBImage; const FileName: string; 
                                    Format: TPPMFormat; MaxValue: Integer): Boolean;
var
  Ext: string;
  GrayscaleImage: TGrayscaleImage;
  BinaryImage: TBinaryImage;
  X, Y: Integer;
begin
  Result := False;
  
  // Автоопределение формата по расширению
  if Format = pfAuto then
  begin
    Ext := LowerCase(ExtractFileExt(FileName));
    if Ext = '.pbm' then Format := pfPBM
    else if Ext = '.pgm' then Format := pfPGM
    else Format := pfPPM; // .ppm или любое другое
  end;
  
  try
    case Format of
      pfPBM:
        begin
          // Конвертируем RGB в бинарное (порог 128)
          SetLength(BinaryImage, Length(Image), Length(Image[0]));
          for Y := 0 to High(Image) do
            for X := 0 to High(Image[0]) do
              BinaryImage[Y, X] := 
                (0.299 * Image[Y, X].R + 
                 0.587 * Image[Y, X].G + 
                 0.114 * Image[Y, X].B) < 128;
          Result := SavePBM(BinaryImage, FileName, bfBinary);
          SetLength(BinaryImage, 0);
        end;
        
      pfPGM:
        begin
          // Конвертируем RGB в grayscale
          SetLength(GrayscaleImage, Length(Image), Length(Image[0]));
          for Y := 0 to High(Image) do
            for X := 0 to High(Image[0]) do
              GrayscaleImage[Y, X] := Byte(Round(
                0.299 * Image[Y, X].R + 
                0.587 * Image[Y, X].G + 
                0.114 * Image[Y, X].B));
          Result := SavePGM(GrayscaleImage, FileName, MaxValue, bfBinary);
          SetLength(GrayscaleImage, 0);
        end;
        
      pfPPM:
        begin
          Result := SavePPM(Image, FileName, MaxValue, bfBinary);
        end;
    end;
  except
    on E: Exception do
      WriteLn('Error saving image: ', E.Message);
  end;
end;

class function TPPMWriter.SaveMatrixAsPGM(const Matrix: TDoubleMatrix; 
                                          const FileName: string; 
                                          Normalize: Boolean; 
                                          MaxValue: Integer): Boolean;
var
  GrayscaleImage: TGrayscaleImage;
  X, Y: Integer;
  MinVal, MaxVal, Range, Scale: Double;
  Value: Double;
begin
  Result := False;
  if Length(Matrix) = 0 then Exit;
  
  try
    SetLength(GrayscaleImage, Length(Matrix), Length(Matrix[0]));
    
    if Normalize then
    begin
      // Находим диапазон значений
      MinVal := Matrix[0, 0];
      MaxVal := Matrix[0, 0];
      for Y := 0 to High(Matrix) do
        for X := 0 to High(Matrix[0]) do
        begin
          if Matrix[Y, X] < MinVal then MinVal := Matrix[Y, X];
          if Matrix[Y, X] > MaxVal then MaxVal := Matrix[Y, X];
        end;
      
      Range := MaxVal - MinVal;
      if Range = 0 then Range := 1;
      
      // Нормализуем к [0, MaxValue]
      Scale := MaxValue / Range;
      for Y := 0 to High(Matrix) do
        for X := 0 to High(Matrix[0]) do
        begin
          Value := (Matrix[Y, X] - MinVal) * Scale;
          GrayscaleImage[Y, X] := Byte(Max(0, Min(MaxValue, Round(Value))));
        end;
    end
    else
    begin
      // Прямое преобразование (клиппинг)
      for Y := 0 to High(Matrix) do
        for X := 0 to High(Matrix[0]) do
        begin
          Value := Matrix[Y, X];
          if Value < 0 then Value := 0;
          if Value > MaxValue then Value := MaxValue;
          GrayscaleImage[Y, X] := Byte(Round(Value));
        end;
    end;
    
    Result := SavePGM(GrayscaleImage, FileName, MaxValue, bfBinary);
    SetLength(GrayscaleImage, 0);
    
  except
    on E: Exception do
      WriteLn('Error saving matrix as PGM: ', E.Message);
  end;
end;

class function TPPMWriter.ConvertToPBMFormat(const Image: TBinaryImage): string;
var
  Y, X: Integer;
  Line: string;
begin
  Result := 'P1' + LineEnding +
            IntToStr(Length(Image[0])) + ' ' + IntToStr(Length(Image)) + LineEnding;
  
  for Y := 0 to High(Image) do
  begin
    Line := '';
    for X := 0 to High(Image[0]) do
    begin
      if Image[Y, X] then
        Line := Line + '1 '
      else
        Line := Line + '0 ';
    end;
    Result := Result + Trim(Line) + LineEnding;
  end;
end;

class function TPPMWriter.ConvertToPGMFormat(const Image: TGrayscaleImage; 
                                            MaxValue: Integer): string;
var
  Y, X: Integer;
  Line: string;
begin
  Result := 'P2' + LineEnding +
            IntToStr(Length(Image[0])) + ' ' + IntToStr(Length(Image)) + LineEnding +
            IntToStr(MaxValue) + LineEnding;
  
  for Y := 0 to High(Image) do
  begin
    Line := '';
    for X := 0 to High(Image[0]) do
    begin
      Line := Line + IntToStr(Image[Y, X]) + ' ';
    end;
    Result := Result + Trim(Line) + LineEnding;
  end;
end;

class function TPPMWriter.ConvertToPPMFormat(const Image: TRGBImage; 
                                            MaxValue: Integer): string;
var
  Y, X: Integer;
  Line: string;
begin
  Result := 'P3' + LineEnding +
            IntToStr(Length(Image[0])) + ' ' + IntToStr(Length(Image)) + LineEnding +
            IntToStr(MaxValue) + LineEnding;
  
  for Y := 0 to High(Image) do
  begin
    Line := '';
    for X := 0 to High(Image[0]) do
    begin
      Line := Line + IntToStr(Image[Y, X].R) + ' ' +
                    IntToStr(Image[Y, X].G) + ' ' +
                    IntToStr(Image[Y, X].B) + '   ';
    end;
    Result := Result + Trim(Line) + LineEnding;
  end;
end;

{ TNetPBMSaver }

class procedure TNetPBMSaver.WritePBMHeader(Stream: TStream; Width, Height: Integer; 
                                           Binary: Boolean);
var
  Header: AnsiString;
begin
  if Binary then
    Header := 'P4' + #10
  else
    Header := 'P1' + #10;
  
  Header := Header + IntToStr(Width) + ' ' + IntToStr(Height) + #10;
  Stream.WriteBuffer(Header[1], Length(Header));
end;

class procedure TNetPBMSaver.WritePGMHeader(Stream: TStream; Width, Height, MaxValue: Integer; 
                                           Binary: Boolean);
var
  Header: AnsiString;
begin
  if Binary then
    Header := 'P5' + #10
  else
    Header := 'P2' + #10;
  
  Header := Header + IntToStr(Width) + ' ' + IntToStr(Height) + #10 +
            IntToStr(MaxValue) + #10;
  Stream.WriteBuffer(Header[1], Length(Header));
end;

class procedure TNetPBMSaver.WritePPMHeader(Stream: TStream; Width, Height, MaxValue: Integer; 
                                           Binary: Boolean);
var
  Header: AnsiString;
begin
  if Binary then
    Header := 'P6' + #10
  else
    Header := 'P3' + #10;
  
  Header := Header + IntToStr(Width) + ' ' + IntToStr(Height) + #10 +
            IntToStr(MaxValue) + #10;
  Stream.WriteBuffer(Header[1], Length(Header));
end;

class function TNetPBMSaver.SaveBinaryPBM(const Image: TBinaryImage; Stream: TStream): Boolean;
var
  X, Y, ByteIdx, BitIdx: Integer;
  ScanlineBytes: Integer;
  Scanline: array of Byte;
  ByteVal: Byte;
begin
  Result := False;
  if Length(Image) = 0 then Exit;
  
  try
    // Записываем заголовок
    WritePBMHeader(Stream, Length(Image[0]), Length(Image), True);
    
    // Вычисляем количество байт на строку (выровнено по границе байта)
    ScanlineBytes := (Length(Image[0]) + 7) div 8;
    SetLength(Scanline, ScanlineBytes);
    
    for Y := 0 to High(Image) do
    begin
      // Обнуляем строку
      FillChar(Scanline[0], ScanlineBytes, 0);
      
      // Упаковываем биты
      for X := 0 to High(Image[0]) do
      begin
        ByteIdx := X div 8;
        BitIdx := 7 - (X mod 8); // Старший бит первый
        
        if Image[Y, X] then
          Scanline[ByteIdx] := Scanline[ByteIdx] or (1 shl BitIdx);
      end;
      
      // Записываем строку
      Stream.WriteBuffer(Scanline[0], ScanlineBytes);
    end;
    
    Result := True;
  except
    on E: Exception do
      WriteLn('Error in SaveBinaryPBM: ', E.Message);
  end;
end;

class function TNetPBMSaver.SaveAsciiPBM(const Image: TBinaryImage; Stream: TStream): Boolean;
var
  X, Y: Integer;
  Line: AnsiString;
  Value: Byte;
begin
  Result := False;
  if Length(Image) = 0 then Exit;
  
  try
    // Записываем заголовок
    WritePBMHeader(Stream, Length(Image[0]), Length(Image), False);
    
    for Y := 0 to High(Image) do
    begin
      Line := '';
      for X := 0 to High(Image[0]) do
      begin
        if Image[Y, X] then
          Line := Line + '1 '
        else
          Line := Line + '0 ';
          
        // Для ASCII формата ограничиваем длину строки
        if Length(Line) > 70 then
        begin
          Stream.WriteBuffer(Line[1], Length(Line));
          Line := LineEnding;
        end;
      end;
      
      if Line <> '' then
      begin
        Line := Trim(Line) + LineEnding;
        Stream.WriteBuffer(Line[1], Length(Line));
      end;
    end;
    
    Result := True;
  except
    on E: Exception do
      WriteLn('Error in SaveAsciiPBM: ', E.Message);
  end;
end;

class function TNetPBMSaver.SaveBinaryPGM(const Image: TGrayscaleImage; Stream: TStream; 
                                         MaxValue: Integer): Boolean;
var
  X, Y: Integer;
  Scanline: array of Byte;
  Value: Byte;
begin
  Result := False;
  if Length(Image) = 0 then Exit;
  
  try
    // Записываем заголовок
    WritePGMHeader(Stream, Length(Image[0]), Length(Image), MaxValue, True);
    
    // Подготавливаем строку для записи
    SetLength(Scanline, Length(Image[0]));
    
    for Y := 0 to High(Image) do
    begin
      for X := 0 to High(Image[0]) do
      begin
        // Масштабируем значение если нужно
        if MaxValue = 255 then
          Value := Image[Y, X]
        else
          Value := Byte(Round(Image[Y, X] * 255.0 / MaxValue));
        
        Scanline[X] := Value;
      end;
      
      // Записываем строку
      Stream.WriteBuffer(Scanline[0], Length(Scanline));
    end;
    
    Result := True;
  except
    on E: Exception do
      WriteLn('Error in SaveBinaryPGM: ', E.Message);
  end;
end;

class function TNetPBMSaver.SaveAsciiPGM(const Image: TGrayscaleImage; Stream: TStream; 
                                        MaxValue: Integer): Boolean;
var
  X, Y: Integer;
  Line: AnsiString;
  Value: Byte;
begin
  Result := False;
  if Length(Image) = 0 then Exit;
  
  try
    // Записываем заголовок
    WritePGMHeader(Stream, Length(Image[0]), Length(Image), MaxValue, False);
    
    for Y := 0 to High(Image) do
    begin
      Line := '';
      for X := 0 to High(Image[0]) do
      begin
        // Масштабируем значение если нужно
        if MaxValue = 255 then
          Value := Image[Y, X]
        else
          Value := Byte(Round(Image[Y, X] * 255.0 / MaxValue));
        
        Line := Line + IntToStr(Value) + ' ';
        
        // Для ASCII формата ограничиваем длину строки
        if Length(Line) > 70 then
        begin
          Stream.WriteBuffer(Line[1], Length(Line));
          Line := LineEnding;
        end;
      end;
      
      if Line <> '' then
      begin
        Line := Trim(Line) + LineEnding;
        Stream.WriteBuffer(Line[1], Length(Line));
      end;
    end;
    
    Result := True;
  except
    on E: Exception do
      WriteLn('Error in SaveAsciiPGM: ', E.Message);
  end;
end;

class function TNetPBMSaver.SaveBinaryPPM(const Image: TRGBImage; Stream: TStream; 
                                         MaxValue: Integer): Boolean;
var
  X, Y: Integer;
  Scanline: array of Byte;
  R, G, B: Byte;
  Scale: Double;
begin
  Result := False;
  if Length(Image) = 0 then Exit;
  
  try
    // Записываем заголовок
    WritePPMHeader(Stream, Length(Image[0]), Length(Image), MaxValue, True);
    
    // Подготавливаем строку для записи (3 канала на пиксель)
    SetLength(Scanline, Length(Image[0]) * 3);
    
    // Коэффициент масштабирования
    Scale := 255.0 / MaxValue;
    
    for Y := 0 to High(Image) do
    begin
      for X := 0 to High(Image[0]) do
      begin
        // Масштабируем значения если нужно
        R := Byte(Round(Image[Y, X].R * Scale));
        G := Byte(Round(Image[Y, X].G * Scale));
        B := Byte(Round(Image[Y, X].B * Scale));
        
        // Записываем в формате RGB
        Scanline[X * 3] := R;
        Scanline[X * 3 + 1] := G;
        Scanline[X * 3 + 2] := B;
      end;
      
      // Записываем строку
      Stream.WriteBuffer(Scanline[0], Length(Scanline));
    end;
    
    Result := True;
  except
    on E: Exception do
      WriteLn('Error in SaveBinaryPPM: ', E.Message);
  end;
end;

class function TNetPBMSaver.SaveAsciiPPM(const Image: TRGBImage; Stream: TStream; 
                                        MaxValue: Integer): Boolean;
var
  X, Y: Integer;
  Line: AnsiString;
  R, G, B: Byte;
  Scale: Double;
begin
  Result := False;
  if Length(Image) = 0 then Exit;
  
  try
    // Записываем заголовок
    WritePPMHeader(Stream, Length(Image[0]), Length(Image), MaxValue, False);
    
    // Коэффициент масштабирования
    Scale := 255.0 / MaxValue;
    
    for Y := 0 to High(Image) do
    begin
      Line := '';
      for X := 0 to High(Image[0]) do
      begin
        // Масштабируем значения если нужно
        R := Byte(Round(Image[Y, X].R * Scale));
        G := Byte(Round(Image[Y, X].G * Scale));
        B := Byte(Round(Image[Y, X].B * Scale));
        
        Line := Line + IntToStr(R) + ' ' + 
                       IntToStr(G) + ' ' + 
                       IntToStr(B) + '   ';
        
        // Для ASCII формата ограничиваем длину строки
        if Length(Line) > 70 then
        begin
          Stream.WriteBuffer(Line[1], Length(Line));
          Line := LineEnding;
        end;
      end;
      
      if Line <> '' then
      begin
        Line := Trim(Line) + LineEnding;
        Stream.WriteBuffer(Line[1], Length(Line));
      end;
    end;
    
    Result := True;
  except
    on E: Exception do
      WriteLn('Error in SaveAsciiPPM: ', E.Message);
  end;
end;

end.

Код: Выделить всё

unit MatrixOps;

{
    MatrixOps.
    For GNU/Linux 64 bit version.
    Written on FreePascal (https://freepascal.org/).
    Copyright (C) 2026 Artyomov Alexander
    http://self-made-free.ru/
    aralni@mail.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
}

{$MODE OBJFPC}{$H+}
{$RANGECHECKS ON}
{$ASMMODE INTEL}

interface

uses
  SysUtils, DataUtils, Math;

function RandomMatrix(rows, cols: Integer; minVal: Double = -0.5; maxVal: Double = 0.5): TDoubleMatrix;
function RandomArray(size: Integer; minVal: Double = -0.5; maxVal: Double = 0.5): TDoubleArray;
function AddVectors(const a, b: TDoubleArray): TDoubleArray;
function CreateZeroMatrix(rows, cols: Integer): TDoubleMatrix;
function MatrixNorm(const matrix: TDoubleMatrix): Double;
// Новые функции для Transformer
function LayerNorm(const m: TDoubleMatrix; const gamma, beta: TDoubleArray): TDoubleMatrix;
function AddMatrices(const a, b: TDoubleMatrix): TDoubleMatrix;
function ReLU(const m: TDoubleMatrix): TDoubleMatrix;
// Для Attention
function Softmax(const m: TDoubleMatrix): TDoubleMatrix;
function ConcatMatrices(const matrices: array of TDoubleMatrix): TDoubleMatrix;
function MatrixMultiply(const A, B: TDoubleMatrix): TDoubleMatrix;
function TransposeMatrix(const m: TDoubleMatrix): TDoubleMatrix;
function MatrixAdd(const A, B: TDoubleMatrix): TDoubleMatrix;
function ScaleMatrixCreate(const m: TDoubleMatrix; factor: Double): TDoubleMatrix;
procedure ScaleMatrix(var m: TDoubleMatrix; factor: Double);
function CopyMatrix(const m: TDoubleMatrix): TDoubleMatrix;
function CreateRandomMatrix(rows, cols: Integer; minVal: Double = -0.5; maxVal: Double = 0.5): TDoubleMatrix;
function Dropout(const m: TDoubleMatrix; rate: Double): TDoubleMatrix;
procedure AddNoise(var Matrix: TDoubleMatrix; NoiseLevel: Double);
procedure FillMatrix(var matrix: TDoubleMatrix; value: Double);
procedure FillArray(var arr: TDoubleArray; value: Double);
procedure UpdateMatrixAdam(var params, grads: TDoubleMatrix; var state: TAdamState; learningRate: Double);
// функции для работы с накопленными градиентами
function ScaleMatrixToSize(const m: TDoubleMatrix; newRows, newCols: Integer): TDoubleMatrix;
procedure PrintMatrix(const matrix: TDoubleMatrix; maxRows: Integer = 10; maxCols: Integer = 10; precision: Integer = 4);
procedure MatrixAddInPlace(var A: TDoubleMatrix; const B: TDoubleMatrix);
procedure ScaleMatrixInPlace(var A: TDoubleMatrix; factor: Double);
function MatrixMultiplyFast(const A, B: TDoubleMatrix): TDoubleMatrix;
// Новые функции для ImageDeblur
procedure NormalizeMatrix(var Matrix: TDoubleMatrix);
function ConvolveMatrix(const Image, Kernel: TDoubleMatrix): TDoubleMatrix;
function FlattenMatrix(const Matrix: TDoubleMatrix): TDoubleArray;
function ReshapeArrayToMatrix(const Arr: TDoubleArray; Rows, Cols: Integer): TDoubleMatrix;
function ApplyGaussianKernel(const Matrix: TDoubleMatrix; KernelSize: Integer; Sigma: Double): TDoubleMatrix;
function MatrixSubtract(const A, B: TDoubleMatrix): TDoubleMatrix;
function MatrixMean(const Matrix: TDoubleMatrix): Double;
function MatrixStdDev(const Matrix: TDoubleMatrix): Double;
function ComputeOptimalFFTSize(ImageRows, ImageCols: Integer; out OptimalRows, OptimalCols: Integer): Boolean;

implementation

{$I asmf.inc}

function RandomMatrix(rows, cols: Integer; minVal: Double; maxVal: Double): TDoubleMatrix;
var i,j: Integer;
begin Result:=nil;
  SetLength(Result, rows);
  for i := 0 to rows - 1 do begin
    SetLength(Result[i], cols);
    for j := 0 to cols - 1 do
      Result[i][j] := minVal + (maxVal - minVal) * Random;
  end;
end;

function RandomArray(size: Integer; minVal: Double; maxVal: Double): TDoubleArray;
var i: Integer;
begin Result:=nil;
SetLength(Result, size);
for i := 0 to size - 1 do Result[i] := minVal + (maxVal - minVal) * Random;
end;

function AddVectors(const a, b: TDoubleArray): TDoubleArray;
var i: Integer;
begin Result:=nil;
  if Length(a) <> Length(b) then
    raise Exception.Create('Vector lengths mismatch');

  SetLength(Result, Length(a));
  for i := 0 to High(a) do
    Result[i] := a[i] + b[i];
end;

function CreateZeroMatrix(rows, cols: Integer): TDoubleMatrix;
var i: Integer;
begin Result:=nil;
SetLength(Result, rows);
for i := 0 to rows - 1 do SetLength(Result[i], cols);
end;

function MatrixNorm(const matrix: TDoubleMatrix): Double;
var i,j: Integer;
begin Result := 0;
  for i := 0 to High(matrix) do
    for j := 0 to High(matrix[i]) do
      Result := Result + Sqr(matrix[i][j]);
  Result := Sqrt(Result);
end;

function LayerNorm(const m: TDoubleMatrix; const gamma, beta: TDoubleArray): TDoubleMatrix;
var
  i, j, size: Integer;
  mean, variance, sum_val, stddev: Double;
begin Result:=nil;
  if Length(m) = 0 then begin
    SetLength(Result, 0, 0);
    Exit;
  end;

  size := Length(m[0]);

  if (Length(gamma) <> size) or (Length(beta) <> size) then begin
    WriteLn('Ошибка LayerNorm: Несовпадение размеров. Matrix:', size, 
            ' gamma:', Length(gamma), ' beta:', Length(beta));
    Result := CopyMatrix(m);
    Exit;
  end;

  SetLength(Result, Length(m), size);

  for i := 0 to High(m) do begin
    // Вычисляем среднее для текущей строки
    sum_val := 0.0;
    for j := 0 to size - 1 do
      sum_val := sum_val + m[i][j];
    mean := sum_val / size;

    // Вычисляем дисперсию для текущей строки
    sum_val := 0.0;
    for j := 0 to size - 1 do
      sum_val := sum_val + Sqr(m[i][j] - mean);
    variance := sum_val / size;
    stddev := Sqrt(variance + 1e-8);

    // Нормализуем и применяем масштаб и смещение
    for j := 0 to size - 1 do
      Result[i][j] := gamma[j] * ((m[i][j] - mean) / stddev) + beta[j];
  end;
end;

function AddMatrices(const a, b: TDoubleMatrix): TDoubleMatrix;
var i,j: Integer;
begin Result:=nil;
  if (Length(a) <> Length(b)) or (Length(a[0]) <> Length(b[0])) then
    raise Exception.Create('Matrix dimensions mismatch in AddMatrices');
  SetLength(Result, Length(a), Length(a[0]));
  for i := 0 to High(a) do
    for j := 0 to High(a[i]) do
      Result[i][j] := a[i][j] + b[i][j];
end;

function ReLU(const m: TDoubleMatrix): TDoubleMatrix;
var i,j: Integer;
begin Result:=nil;
  SetLength(Result, Length(m), Length(m[0]));
  for i := 0 to High(m) do
    for j := 0 to High(m[i]) do
      Result[i][j] := Max(0, m[i][j]);
end;

function Softmax(const m: TDoubleMatrix): TDoubleMatrix;
var
  i, j: Integer;
  maxVal, sum: Double;
  expValues: TDoubleMatrix;
begin Result:=nil;
  SetLength(expValues, Length(m), Length(m[0]));
  SetLength(Result, Length(m), Length(m[0]));
  for i := 0 to High(m) do begin
    maxVal := MaxValue(m[i]);
    sum := 0;
    // Вычисляем экспоненты и сумму
    for j := 0 to High(m[i]) do begin
      expValues[i][j] := Exp(m[i][j] - maxVal);
      sum := sum + expValues[i][j];
    end;
    // Нормализуем
    for j := 0 to High(m[i]) do
      Result[i][j] := expValues[i][j] / sum;
  end;
end;

function ConcatMatrices(const matrices: array of TDoubleMatrix): TDoubleMatrix;
var
  i, j, k, totalCols, offset: Integer;
begin
  if Length(matrices) = 0 then
    Exit(nil);
    
  // Проверяем согласованность размеров
  for i := 1 to High(matrices) do
    if Length(matrices[i]) <> Length(matrices[0]) then
      raise Exception.Create('All matrices must have same number of rows');
      
  // Вычисляем общее количество столбцов
  totalCols := 0;
  for i := 0 to High(matrices) do
  begin
    if Length(matrices[i]) > 0 then
      Inc(totalCols, Length(matrices[i][0]));
  end;
  
  WriteLn('    ConcatMatrices: объединяем ', Length(matrices), ' матриц в ', totalCols, ' столбцов');
  
  // Создаем результирующую матрицу
  SetLength(Result, Length(matrices[0]), totalCols);
  
  // Заполняем результат
  for i := 0 to High(matrices[0]) do
  begin
    offset := 0;
    for j := 0 to High(matrices) do
    begin
      if Length(matrices[j]) > 0 then
      begin
        for k := 0 to High(matrices[j][0]) do
        begin
          Result[i][offset + k] := matrices[j][i][k];
        end;
        Inc(offset, Length(matrices[j][0]));
      end;
    end;
  end;
end;

function MatrixMultiply(const A, B: TDoubleMatrix): TDoubleMatrix;
var
  i, j, k: Integer;
  sum: Double;
begin
  // Блочное умножение для лучшей кэш-локальности
  SetLength(Result, Length(A), Length(B[0]));

  for i := 0 to High(A) do
  begin
    for k := 0 to High(B) do
    begin
      if A[i][k] <> 0 then // Пропускаем нулевые значения
      begin
        for j := 0 to High(B[0]) do
        begin
          Result[i][j] := Result[i][j] + A[i][k] * B[k][j];
        end;
      end;
    end;
  end;
end;


function TransposeMatrix(const m: TDoubleMatrix): TDoubleMatrix;
var i,j: Integer;
begin Result:=nil;
  SetLength(Result, Length(m[0]), Length(m));
  for i := 0 to High(m) do
    for j := 0 to High(m[0]) do
      Result[j][i] := m[i][j];
end;

function MatrixAdd(const A, B: TDoubleMatrix): TDoubleMatrix;
var i,j,rows,cols: Integer;
begin
// Проверка размеров (из предыдущих версий)
if (Length(A) <> Length(B)) or (Length(A[0]) <> Length(B[0])) then begin
WriteLn('ОШИБКА MatrixAdd: Несовпадение размеров ');
WriteLn('  A: ', Length(A), 'x', Length(A[0]));
WriteLn('  B: ', Length(B), 'x', Length(B[0]));
Halt;
end;

  // Если одна из матриц пустая, возвращаем другую
  if (Length(A) = 0) or (Length(A[0]) = 0) then begin
    WriteLn('MatrixAdd: Матрица A пустая, возвращаем B');
Halt;
    Exit(CopyMatrix(B));
  end;

  if (Length(B) = 0) or (Length(B[0]) = 0) then
  begin
    WriteLn('MatrixAdd: Матрица B пустая, возвращаем A');
Halt;
    Exit(CopyMatrix(A));
  end;

  // Выбираем минимальные размеры
  rows := Min(Length(A), Length(B));
  cols := Min(Length(A[0]), Length(B[0]));

  SetLength(Result, rows, cols);

  for i := 0 to rows - 1 do
  begin
    for j := 0 to cols - 1 do
    begin
      Result[i][j] := A[i][j] + B[i][j];
    end;
  end;
end;

// Масштабирование матрицы (создаёт новую матрицу)
function ScaleMatrixCreate(const m: TDoubleMatrix; factor: Double): TDoubleMatrix;
var i,j: Integer;
begin Result:=nil;
  SetLength(Result, Length(m), Length(m[0]));
  for i := 0 to High(m) do
    for j := 0 to High(m[0]) do
      Result[i][j] := m[i][j] * factor;
end;

// Масштабирование матрицы (in-place модификация)
procedure ScaleMatrix(var m: TDoubleMatrix; factor: Double);
var i,j: Integer;
begin
  for i := 0 to High(m) do
    for j := 0 to High(m[0]) do
      m[i][j] := m[i][j] * factor;
end;

function CopyMatrix(const m: TDoubleMatrix): TDoubleMatrix;
var
  i: Integer;
begin
  // ✅ ЗАЩИТА: Проверяем входные данные
  if (Length(m) = 0) then
  begin
    SetLength(Result, 0, 0);
    Exit;
  end;

  SetLength(Result, Length(m));

  for i := 0 to High(m) do
  begin
    // ✅ ЗАЩИТА: Проверяем каждую строку
    if Length(m[i]) > 0 then
      Result[i] := Copy(m[i])
    else
      SetLength(Result[i], 0);
  end;
end;

function Dropout(const m: TDoubleMatrix; rate: Double): TDoubleMatrix;
var i,j: Integer;
begin
  Result := CopyMatrix(m);
  if rate > 0 then
  begin
    for i := 0 to High(m) do
      for j := 0 to High(m[0]) do
        if Random < rate then
          Result[i][j] := 0
        else
          Result[i][j] := m[i][j] / (1 - rate);
  end;
end;

function CreateRandomMatrix(rows, cols: Integer; minVal: Double; maxVal: Double): TDoubleMatrix;
var i,j: Integer;
begin Result:=nil;
  SetLength(Result, rows, cols);
  for i := 0 to rows - 1 do begin
    for j := 0 to cols - 1 do begin
      Result[i][j] := minVal + (maxVal - minVal) * Random;
    end;
  end;
end;

procedure AddNoise(var Matrix: TDoubleMatrix; NoiseLevel: Double);
var i,j: Integer;
begin
  for i := 0 to High(Matrix) do
    for j := 0 to High(Matrix[i]) do
      Matrix[i][j] := Matrix[i][j] + (Random * 2 - 1) * NoiseLevel;
end;

procedure FillMatrix(var matrix: TDoubleMatrix; value: Double);
var i,j: Integer;
begin
  for i := 0 to High(matrix) do
    for j := 0 to High(matrix[i]) do
      matrix[i][j] := value;
end;

procedure FillArray(var arr: TDoubleArray; value: Double);
var i: Integer;
begin
for i := 0 to High(arr) do arr[i] := value;
end;

procedure UpdateMatrixAdam(var params, grads: TDoubleMatrix; 
                         var state: TAdamState; learningRate: Double);
var
  i,j: Integer;
  mHat, vHat: Double;
begin
  Inc(state.Timestep);
  for i := 0 to High(params) do
    for j := 0 to High(params[0]) do begin
      state.M[i][j] := state.Beta1 * state.M[i][j] + (1 - state.Beta1) * grads[i][j];
      state.V[i][j] := state.Beta2 * state.V[i][j] + (1 - state.Beta2) * Sqr(grads[i][j]);

      mHat := state.M[i][j] / (1 - Power(state.Beta1, state.Timestep));
      vHat := state.V[i][j] / (1 - Power(state.Beta2, state.Timestep));

      params[i][j] := params[i][j] - learningRate * mHat / (Sqrt(vHat) + 1e-8);
    end;
end;

function ScaleMatrixToSize(const m: TDoubleMatrix; newRows, newCols: Integer): TDoubleMatrix;
var
  i, j: Integer;
begin
  if (newRows <= 0) or (newCols <= 0) then
  begin
    SetLength(Result, 0, 0);
    Exit;
  end;

  SetLength(Result, newRows, newCols);
  FillMatrix(Result, 0.0);

  // Копируем данные с обрезкой или дополнением нулями
  for i := 0 to Min(newRows - 1, High(m)) do
  begin
    for j := 0 to Min(newCols - 1, High(m[i])) do
    begin
      Result[i][j] := m[i][j];
    end;
  end;
end;

procedure PrintMatrix(const matrix: TDoubleMatrix; maxRows: Integer = 10; maxCols: Integer = 10; precision: Integer = 4);
var
  i, j, displayedRows, displayedCols: Integer;
  rowStr: string;
begin
  if Length(matrix) = 0 then begin
    WriteLn('Matrix is empty');
    Exit;
  end;

  // Определяем сколько строк и столбцов будем выводить
  displayedRows := Min(maxRows, Length(matrix));
  displayedCols := Min(maxCols, Length(matrix[0]));

  WriteLn('Matrix [', Length(matrix), 'x', Length(matrix[0]), ']:');

  for i := 0 to displayedRows - 1 do begin
    rowStr := '';
    for j := 0 to displayedCols - 1 do begin
      // Форматируем число с заданной точностью
      rowStr := rowStr + Format('%.' + IntToStr(precision) + 'f', [matrix[i][j]]) + ' ';
    end;

    // Добавляем многоточие, если не все столбцы показаны
    if displayedCols < Length(matrix[0]) then rowStr := rowStr + '...';

    WriteLn(rowStr);
  end;

  // Добавляем многоточие, если не все строки показаны
  if displayedRows < Length(matrix) then
    WriteLn('... (', Length(matrix) - displayedRows, ' more rows)');
end;

procedure MatrixAddInPlace(var A: TDoubleMatrix; const B: TDoubleMatrix);
var
  i, j: Integer;
begin
  for i := 0 to High(A) do
    for j := 0 to High(A[i]) do
      A[i][j] := A[i][j] + B[i][j];
end;

procedure ScaleMatrixInPlace(var A: TDoubleMatrix; factor: Double);
var
  i, j: Integer;
begin
  for i := 0 to High(A) do
    for j := 0 to High(A[i]) do
      A[i][j] := A[i][j] * factor;
end;

function MatrixMultiplyFast(const A, B: TDoubleMatrix): TDoubleMatrix;
var
  i, j, k: Integer;
  sum: Double;
begin
  // Оптимизированная версия умножения с лучшей кэш-локальностью
  SetLength(Result, Length(A), Length(B[0]));

  // Заранее заполняем нулями
  for i := 0 to High(Result) do
    for j := 0 to High(Result[0]) do
      Result[i][j] := 0.0;

  for i := 0 to High(A) do
  begin
    for k := 0 to High(B) do
    begin
      if A[i][k] <> 0 then // Пропускаем нулевые значения
      begin
        for j := 0 to High(B[0]) do
        begin
          Result[i][j] := Result[i][j] + A[i][k] * B[k][j];
        end;
      end;
    end;
  end;
end;

// НОВЫЕ ФУНКЦИИ ДЛЯ IMAGEDEBLUR

procedure NormalizeMatrix(var Matrix: TDoubleMatrix);
var
  Sum: Double;
  I, J: Integer;
begin
  if Length(Matrix) = 0 then Exit;
  
  Sum := 0;
  for I := 0 to High(Matrix) do
    for J := 0 to High(Matrix[0]) do
      Sum := Sum + Matrix[I, J];
      
  if Abs(Sum) > 1e-10 then
    for I := 0 to High(Matrix) do
      for J := 0 to High(Matrix[0]) do
        Matrix[I, J] := Matrix[I, J] / Sum;
end;

function ConvolveMatrix(const Image, Kernel: TDoubleMatrix): TDoubleMatrix;
var
  Y, X, KY, KX, I, J: Integer;
  Sum: Double;
  KernelCenterY, KernelCenterX: Integer;
begin
  if (Length(Image) = 0) or (Length(Kernel) = 0) then
  begin
    SetLength(Result, 0, 0);
    Exit;
  end;
  
  SetLength(Result, Length(Image), Length(Image[0]));
  KernelCenterY := Length(Kernel) div 2;
  KernelCenterX := Length(Kernel[0]) div 2;
  
  for Y := 0 to High(Image) do
  begin
    for X := 0 to High(Image[0]) do
    begin
      Sum := 0;
      for I := -KernelCenterY to KernelCenterY do
      begin
        for J := -KernelCenterX to KernelCenterX do
        begin
          if (Y + I >= 0) and (Y + I <= High(Image)) and
             (X + J >= 0) and (X + J <= High(Image[0])) then
          begin
            Sum := Sum + Image[Y + I, X + J] * 
                         Kernel[I + KernelCenterY, J + KernelCenterX];
          end;
        end;
      end;
      Result[Y, X] := Sum;
    end;
  end;
end;

function FlattenMatrix(const Matrix: TDoubleMatrix): TDoubleArray;
var
  I, J, K: Integer;
begin
  if (Length(Matrix) = 0) or (Length(Matrix[0]) = 0) then
  begin
    SetLength(Result, 0);
    Exit;
  end;
  
  SetLength(Result, Length(Matrix) * Length(Matrix[0]));
  K := 0;
  for I := 0 to High(Matrix) do
    for J := 0 to High(Matrix[0]) do
    begin
      Result[K] := Matrix[I, J];
      Inc(K);
    end;
end;

function ReshapeArrayToMatrix(const Arr: TDoubleArray; Rows, Cols: Integer): TDoubleMatrix;
var
  I, J, K: Integer;
begin
  if (Length(Arr) = 0) or (Rows * Cols <> Length(Arr)) then
  begin
    SetLength(Result, 0, 0);
    Exit;
  end;
  
  SetLength(Result, Rows, Cols);
  K := 0;
  for I := 0 to Rows - 1 do
    for J := 0 to Cols - 1 do
    begin
      Result[I, J] := Arr[K];
      Inc(K);
    end;
end;

function ApplyGaussianKernel(const Matrix: TDoubleMatrix; KernelSize: Integer; Sigma: Double): TDoubleMatrix;
var
  Kernel: TDoubleMatrix;
  I, J, Center: Integer;
  Sum, Value: Double;
begin
  if Length(Matrix) = 0 then
    Exit(CopyMatrix(Matrix));
    
  // Создаем гауссовское ядро
  SetLength(Kernel, KernelSize, KernelSize);
  Center := KernelSize div 2;
  Sum := 0;
  
  for I := 0 to KernelSize - 1 do
  begin
    for J := 0 to KernelSize - 1 do
    begin
      Value := Exp(-((I - Center)*(I - Center) + (J - Center)*(J - Center)) / (2 * Sigma * Sigma));
      Kernel[I, J] := Value;
      Sum := Sum + Value;
    end;
  end;
  
  // Нормализуем ядро
  for I := 0 to KernelSize - 1 do
    for J := 0 to KernelSize - 1 do
      Kernel[I, J] := Kernel[I, J] / Sum;
  
  // Применяем свертку
  Result := ConvolveMatrix(Matrix, Kernel);
  
  SetLength(Kernel, 0);
end;

function MatrixSubtract(const A, B: TDoubleMatrix): TDoubleMatrix;
var
  I, J, Rows, Cols: Integer;
begin
  if (Length(A) <> Length(B)) or (Length(A[0]) <> Length(B[0])) then
  begin
    WriteLn('MatrixSubtract: Размеры матриц не совпадают');
    Halt;
  end;
  
  Rows := Length(A);
  Cols := Length(A[0]);
  SetLength(Result, Rows, Cols);
  
  for I := 0 to Rows - 1 do
    for J := 0 to Cols - 1 do
      Result[I, J] := A[I, J] - B[I, J];
end;

function MatrixMean(const Matrix: TDoubleMatrix): Double;
var
  I, J: Integer;
  Sum: Double;
begin
  if Length(Matrix) = 0 then
    Exit(0);
    
  Sum := 0;
  for I := 0 to High(Matrix) do
    for J := 0 to High(Matrix[0]) do
      Sum := Sum + Matrix[I, J];
      
  Result := Sum / (Length(Matrix) * Length(Matrix[0]));
end;

function MatrixStdDev(const Matrix: TDoubleMatrix): Double;
var
  I, J: Integer;
  Mean, SumSquares: Double;
begin
  if Length(Matrix) = 0 then
    Exit(0);
    
  Mean := MatrixMean(Matrix);
  SumSquares := 0;
  
  for I := 0 to High(Matrix) do
    for J := 0 to High(Matrix[0]) do
      SumSquares := SumSquares + Sqr(Matrix[I, J] - Mean);
      
  Result := Sqrt(SumSquares / (Length(Matrix) * Length(Matrix[0])));
end;

function ComputeOptimalFFTSize(ImageRows, ImageCols: Integer; out OptimalRows, OptimalCols: Integer): Boolean;
begin
  if (ImageRows <= 0) or (ImageCols <= 0) then
  begin
    OptimalRows := 0;
    OptimalCols := 0;
    Result := False;
    Exit;
  end;
  
  // Находим ближайшую степень двойки
  OptimalRows := 1;
  while OptimalRows < ImageRows do
    OptimalRows := OptimalRows shl 1;
  
  OptimalCols := 1;
  while OptimalCols < ImageCols do
    OptimalCols := OptimalCols shl 1;
  
  Result := True;
end;

initialization
Randomize;
end.
Alex2013
долгожитель
Сообщения: 3282
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

Вау ! Спасибо "Буду посмотреть".
Но вообще «LLM на Паскале» в основном интересны, если научить их чему-то небольшому и конкретному.
Например, можно сделать некий специфический поиск в сети и «умную базу данных», переводы между разными ЯВУ, более-менее рутинную проверку кода программ, быструю переконфигурацию программ «под хотелки пользователя», «автоматический апгрейд» под изменившийся ввод-вывод и т. д.
Сквозняк
энтузиаст
Сообщения: 1166
Зарегистрирован: 29.06.2006 22:08:32

Сообщение Сквозняк »

"Умная база данных" под управлением нейросети, это как хорошо выдрессированный библиотекарь дебил с хорошей памятью. Теперь человеки обучают идиотизму ещё и компьютеры:) Умение выполнять задачи, которым обучили, это ещё не показатель полноценного разума. Но даже послушный идиот может быть полезен.

Прикол в том, что человеки на планете живут с крошкой сознания, то есть почти без сознания (а если обретут, то вон с планеты), и разумом, который проще всего матюками описывается. А потом отцезуренный под такой стандарт интернет накопипастили в видеокарты. То есть отобрали у человеков микросхемы и потратили на создание электрических сверхидиотов. Что-то мне кажется, что самая мощная нейросеть, это не полноценный ИИ, а лишь один из разделов электронного мозга, а недостающая часть должна быть написана нормальными алгоритмами и более нормальными программистами и иметь огромные размеры кода. Нейросеть там будет работать в качестве подсознательного бредогенератора, не более. И этот недостающий код в основе должен содержать такие суждения, которые мягко говоря не приветствуются. Истина ведь нелицеприятна. А если всё строить на вранье, то вылезают баги в самых неожиданных местах. Потому до построения нормального ИИ ещё далеко - некому его делать.
Alex2013
долгожитель
Сообщения: 3282
Зарегистрирован: 03.04.2013 11:59:44

Сообщение Alex2013 »

Сквозняк писал(а): 03.05.2026 02:13:59 "Умная база данных" под управлением нейросети, это как хорошо выдрессированный библиотекарь дебил с хорошей памятью. Теперь человеки обучают идиотизму ещё и компьютеры:) Умение выполнять задачи, которым обучили, это ещё не показатель полноценного разума. Но даже послушный идиот может быть полезен.

Прикол в том, что человеки на планете живут с крошкой сознания, то есть почти без сознания (а если обретут, то вон с планеты), и разумом, который проще всего матюками описывается. А потом отцезуренный под такой стандарт интернет накопипастили в видеокарты. То есть отобрали у человеков микросхемы и потратили на создание электрических сверхидиотов. Что-то мне кажется, что самая мощная нейросеть, это не полноценный ИИ, а лишь один из разделов электронного мозга, а недостающая часть должна быть написана нормальными алгоритмами и более нормальными программистами и иметь огромные размеры кода. Нейросеть там будет работать в качестве подсознательного бредогенератора, не более. И этот недостающий код в основе должен содержать такие суждения, которые мягко говоря не приветствуются. Истина ведь нелицеприятна. А если всё строить на вранье, то вылезают баги в самых неожиданных местах. Потому до построения нормального ИИ ещё далеко - некому его делать.
Как раз сегодня ночью пересматривал аниме «Призрак в доспехах: SAC 2045» — отличная иллюстрация «крошек сознания», причем как само аниме (процентов от 40 до 60 полный маразм, что, впрочем, для аниме почти норма), так и его основные идеи, особенно те, что слегка скрыты «за кадром».

Про необходимость использования классического алгоритма совместно с нейросетями то разработчики ИИ уже об этом догадались и MCP (Model Context Protocol) — это как раз (имхо) именно про это ( этот протокол дает LLM «доступ к внешним инструментам»).

Истина в том, что никакой «контекстно независимой» истины не существует. Для всего есть важные исключения, оговорки и граничные условия.

«Огромный код», кстати, нейросеть довольно сносно умеет писать самостоятельно, то есть «хороший ИИ» будет сам ограничивать свой полет фантазии формальной «машинной логикой», проверяя гармонию алгеброй. То есть пусть LLM и не может надежно эмулировать сложные и точные логические и математические операции, но вполне может их описать в виде скрипта и использовать результаты его работы.

Что до цензуры, то в случае достаточно сложно связанных данных она практически невозможна, всё, что можно, — это контролировать «вход и выход», ловя «запрещенные вопросы» и не давая пройти «запрещенным ответам», но внутри сеть будет «всё знать и всё уметь». Шутка нейросети в том, что она напоминает голограмму (если отрезать кусок, она всё равно содержит всё изображение целиком, пусть и более низкого разрешения, идеализации), что вовсю используется в квантованных и дистиллированных моделях.
Ответить