Fixing Unicode Conversion Error in Pascal Code
MatrixOps.pas
PAS 15.94KB
ucs4unit.pas
PAS 8.78KB
ucs4functionsunit.pas
PAS 14.15KB
word2vectranslator.pas
PAS 8.23KB
DataUtils.pas
PAS 6.48KB
ucs4opunit.pas
PAS 1.79KB
ucs4multilineunit.pas
PAS 3.48KB
Word2Vec.pas
PAS 10.42KB
Привет! В прошлом чате мы делали переводчик на основе Word2Vec. Давай продолжим это. Я запустил его и он выдал: ./word2vectranslator ~/MODELS/en/cc.en.300.vec ./word2vec.model z.txt zo.txt
Word2Vec Translator v1.0
=======================
Настройки:
  Порог схожести: 0.70
  Кандидатов: 3

Загрузка исходной модели: /home/first/MODELS/en/cc.en.300.vec
Исходная модель загружена за 13:03.743
Словарь: 2000000 слов
Загрузка целевой модели: ./word2vec.model
Целевая модель загружена за 28:23.106
Словарь: 2000000 слов
Загрузка исходного текста: z.txt
Загружено строк: 1
Начало перевода...
Перевод завершен за 05:15.292
Сохранение перевода: zo.txt
Ошибка: UnicodeToUTF8: invalid unicode: 2052601624

Привет! Отлично, что продолжили работу с переводчиком. Ошибка "UnicodeToUTF8: invalid unicode: 2052601624" указывает на проблему с преобразованием Unicode-символа в UTF-8. Давайте разберемся, почему это происходит и как исправить.
Анализ проблемы

Ошибка возникает в функции UnicodeToUTF8 при попытке преобразовать число 2052601624 в UTF-8. Это значение выходит за допустимые пределы для Unicode символов (максимальное значение - 0x10FFFF).

Скорее всего, это происходит потому, что:

    Модели Word2Vec содержат слова, которые не являются валидными Unicode символами

    Возможно, есть проблемы с чтением/парсингом моделей

    Могут быть бинарные или некорректные данные в моделях

Решение

Добавим защиту от некорректных Unicode значений в функции преобразования. Вот исправленная версия:
pascal

// В unit ucs4unit, исправляем функцию ToUTF8
function ucs4.ToUTF8: string;
var
  I, Len: Integer;
  P: PChar;
  CodePoint: DWord;
begin
  if FLength = 0 then Exit('');

  // Максимально возможный размер (4 байта на символ)
  SetLength(Result, FLength * 4);
  P := PChar(Result);
  
  for I := 0 to FLength - 1 do
  begin
    CodePoint := FData[I];
    
    // Проверяем валидность Unicode code point
    if (CodePoint <= $10FFFF) and 
       ((CodePoint < $D800) or (CodePoint > $DFFF)) then // Исключаем surrogate pairs
    begin
      Inc(P, UnicodeToUTF8(CodePoint, P));
    end
    else
    begin
      // Заменяем невалидные символы на U+FFFD (REPLACEMENT CHARACTER)
      Inc(P, UnicodeToUTF8($FFFD, P));
    end;
  end;
  
  // Корректируем длину под реальный размер
  SetLength(Result, P - PChar(Result));
end;

Также добавим проверку в функцию UnicodeToUTF8 (если она у вас реализована отдельно):
pascal

function UnicodeToUTF8(CodePoint: DWord; Buffer: PChar): Integer;
begin
  // Проверка на валидность Unicode code point
  if (CodePoint > $10FFFF) or 
     ((CodePoint >= $D800) and (CodePoint <= $DFFF)) then
  begin
    CodePoint := $FFFD; // REPLACEMENT CHARACTER
  end;

  // Стандартная реализация преобразования...
  if CodePoint <= $7F then
  begin
    Buffer[0] := Char(CodePoint);
    Result := 1;
  end
  else if CodePoint <= $7FF then
  begin
    Buffer[0] := Char($C0 or (CodePoint shr 6));
    Buffer[1] := Char($80 or (CodePoint and $3F));
    Result := 2;
  end
  else if CodePoint <= $FFFF then
  begin
    Buffer[0] := Char($E0 or (CodePoint shr 12));
    Buffer[1] := Char($80 or ((CodePoint shr 6) and $3F));
    Buffer[2] := Char($80 or (CodePoint and $3F));
    Result := 3;
  end
  else
  begin
    Buffer[0] := Char($F0 or (CodePoint shr 18));
    Buffer[1] := Char($80 or ((CodePoint shr 12) and $3F));
    Buffer[2] := Char($80 or ((CodePoint shr 6) and $3F));
    Buffer[3] := Char($80 or (CodePoint and $3F));
    Result := 4;
  end;
end;

Дополнительные улучшения

Также рекомендую добавить логирование для отладки:
pascal

function TranslateWord(SourceEmbeddings, TargetEmbeddings: TWordEmbeddings; 
                      const Word: ucs4; Threshold: Double; MaxCandidates: Integer): ucs4;
var
  // ... существующий код ...
begin
  // Добавим отладочный вывод
  Writeln('Перевод слова: ', Word.ToUTF8);
  
  try
    // ... существующий код ...
    
    if BestIndex >= 0 then
      Writeln('Выбрано: ', SimilarWords[BestIndex], ' (схожесть: ', BestScore:0:3, ')')
    else
      Writeln('Перевод не найден, оставляем оригинал');
      
  except
    on E: Exception do
    begin
      Writeln('Ошибка при переводе слова "', Word.ToUTF8, '": ', E.Message);
      Result := Word; // Возвращаем исходное слово при ошибке
    end;
  end;
end;

Проверка моделей

Также проверьте, что ваши модели загружаются корректно. Может быть полезно добавить проверку:
pascal

constructor TWordEmbeddings.Create(const ModelFile: string);
var
  // ... существующий код ...
begin
  // ... существующий код ...
  
  for i := 0 to FVocab.Capacity - 1 do
  begin
    if EOF(F) then
      raise Exception.Create('Неожиданный конец файла модели');
      
    ReadLn(F, Line);
    
    // Проверяем, что строка не пустая
    if Line = '' then Continue;
    
    Parts := Line.Split([' ']);
    
    // Проверяем корректность данных
    if Length(Parts) < FEmbeddingSize + 1 then
    begin
      Writeln('Предупреждение: пропущена некорректная строка в модели: ', Line);
      Continue;
    end;
    
    // ... обработка ...
  end;
end;

После внесения этих изменений перекомпилируйте программу и попробуйте запустить снова. Защита от невалидных Unicode символов должна решить проблему.
Я закомментировал строку TranslatedLine.Clear;, добавил отладочные сообщения и он запустился. Похоже в русской модели есть несколько и английских слов и он их в первую очередь ищет: ./word2vectranslator ~/MODELS/en/cc.en.300.vec ./word2vec.model z.txt zo.txt
Word2Vec Translator v1.0
=======================
Настройки:
  Порог схожести: 0.70
  Кандидатов: 3

Загрузка исходной модели: /home/first/MODELS/en/cc.en.300.vec
Исходная модель загружена за 13:16.591
Словарь: 2000000 слов
Загрузка целевой модели: ./word2vec.model
Целевая модель загружена за 27:14.386
Словарь: 2000000 слов
Загрузка исходного текста: z.txt
ucs4multiline.LoadFromFile: Загружается файл: z.txt
ucs4multiline.LoadFromFile: Файл содержит 5 строк (предварительно)
ucs4multiline.LoadFromFile: Файл загружен, содержит 5 строк
Загружено строк: 5
Начало перевода...
Debug:  Tokens[i] = hello
Debug:  LowerWord := ToLower(Word); = hello
Debug:  SimilarWords[i] = Oh
Debug:  SimilarWords[i] = Hi
Debug:  SimilarWords[i] = My
Debug:  Result = Hi
Debug:  TranslatedTokens[i] = Hi
Debug: (Собираем обратно в предложение) Result = Hi
>>> Hi
Debug:  Tokens[i] = Alien
Debug:  Tokens[i] = can
Debug:  Tokens[i] = use
Debug:  Tokens[i] = special
Debug:  Tokens[i] = diff
Debug:  Tokens[i] = files
Debug:  Tokens[i] = to
Debug:  Tokens[i] = help
Debug:  Tokens[i] = make
Debug:  Tokens[i] = alien
Debug:  Tokens[i] = packages
Debug:  Tokens[i] = conform
Debug:  Tokens[i] = to
Debug:  LowerWord := ToLower(Word); = alien
Debug:  SimilarWords[i] = Of
Debug:  SimilarWords[i] = Vs
Debug:  SimilarWords[i] = Ol
Debug:  Result = Alien
Debug:  LowerWord := ToLower(Word); = can
Debug:  SimilarWords[i] = we
Debug:  SimilarWords[i] = can
Debug:  SimilarWords[i] = be
Debug:  Result = can
Debug:  LowerWord := ToLower(Word); = use
Debug:  SimilarWords[i] = use
Debug:  SimilarWords[i] = be
Debug:  SimilarWords[i] = to
Debug:  Result = use
Debug:  LowerWord := ToLower(Word); = special
Debug:  SimilarWords[i] = Nm
Debug:  SimilarWords[i] = XE
Debug:  SimilarWords[i] = 7a
Debug:  Result = special
Debug:  LowerWord := ToLower(Word); = diff
Debug:  SimilarWords[i] = diff
Debug:  SimilarWords[i] = oc
Debug:  SimilarWords[i] = ✹
Debug:  Result = diff
Debug:  LowerWord := ToLower(Word); = files
Debug:  SimilarWords[i] = РІ
Debug:  SimilarWords[i] = 7a
Debug:  SimilarWords[i] = hh
Debug:  Result = files
Debug:  LowerWord := ToLower(Word); = to
^C

Отлично, что программа запустилась! Я вижу очень полезную отладочную информацию. Проблема стала понятна - переводчик действительно находит английские слова в русской модели, но качество перевода низкое.
Основные проблемы:

    Смешанный словарь: Русская модель содержит много английских слов

    Низкое качество кандидатов: Находятся слова типа "Of", "Vs", "Ol" вместо нормальных переводов

    Порог слишком низкий: 0.7 позволяет проходить плохим переводам

Решения:
1. Повысим порог схожести и улучшим фильтрацию
pascal

function TranslateWord(SourceEmbeddings, TargetEmbeddings: TWordEmbeddings; 
                      const Word: ucs4; Threshold: Double; MaxCandidates: Integer): ucs4;
var
  LowerWord: ucs4;
  SimilarWords: TStringArray;
  SimilarityScores: array of Double;
  BestScore: Double;
  BestIndex: Integer;
  i: Integer;
  WordStr: string;
begin
  WordStr := Word.ToUTF8;
  Writeln('Перевод слова: "', WordStr, '"');
  
  // Пропускаем короткие слова и знаки препинания
  if (Word.Length <= 2) or IsPunctuation(Word[0]) then
  begin
    Writeln('  Пропускаем (короткое слово или пунктуация)');
    Result := Word;
    Exit;
  end;

  LowerWord := ToLower(Word);
  
  try
    SimilarWords := TargetEmbeddings.MostSimilar(LowerWord.ToUTF8, MaxCandidates * 2); // Берем больше кандидатов
    
    if Length(SimilarWords) = 0 then
    begin
      Writeln('  Нет похожих слов в целевой модели');
      Result := Word;
      Exit;
    end;

    // Фильтруем кандидатов
    SetLength(SimilarityScores, Length(SimilarWords));
    BestScore := 0;
    BestIndex := -1;

    for i := 0 to High(SimilarWords) do
    begin
      SimilarityScores[i] := SourceEmbeddings.FastSimilarity(
        LowerWord.ToUTF8, SimilarWords[i]);
      
      Writeln('  Кандидат "', SimilarWords[i], '": ', SimilarityScores[i]:0:3);
      
      // Дополнительная проверка: отбрасываем слишком короткие или неподходящие слова
      if (SimilarityScores[i] > BestScore) and 
         (Length(SimilarWords[i]) >= 2) and // Минимальная длина
         (SimilarityScores[i] >= Threshold) then
      begin
        BestScore := SimilarityScores[i];
        BestIndex := i;
      end;
    end;

    if (BestIndex >= 0) and (BestScore >= Threshold) then
    begin
      Result := SimilarWords[BestIndex];
      Writeln('  Выбрано: "', Result, '" (схожесть: ', BestScore:0:3, ')');
    end
    else
    begin
      Writeln('  Подходящий перевод не найден (лучший score: ', BestScore:0:3, ')');
      Result := Word;
    end;

  finally
    LowerWord.Clear;
  end;
end;

2. Увеличим порог в основном коде
pascal

// В ParseCommandLine измените значение по умолчанию
Result.TranslationThreshold := StrToFloatDef(GetEnvironmentVariable('TRANS_THRESHOLD'), 0.85); // Было 0.7

3. Добавим фильтрацию по языку (опционально)
pascal

function IsLikelyRussian(const Word: string): Boolean;
var
  i: Integer;
  c: Char;
begin
  // Простая эвристика для определения русских слов
  for i := 1 to Length(Word) do
  begin
    c := Word[i];
    // Русские буквы в UTF-8 диапазоне
    if (c >= #$D0) and (c <= #$DF) then
      Exit(True);
    if (c >= #$E0) and (c <= #$EF) then
      Exit(True);
  end;
  Result := False;
end;

// В TranslateWord добавьте проверку:
if (BestIndex >= 0) and (BestScore >= Threshold) and 
   IsLikelyRussian(SimilarWords[BestIndex]) then
begin
  Result := SimilarWords[BestIndex];
end
else if (BestIndex >= 0) and (BestScore >= Threshold * 1.2) then 
// Для не-русских слов требуем更高的 порог
begin
  Result := SimilarWords[BestIndex];
end;

4. Запустите с новыми параметрами
bash

TRANS_THRESHOLD=0.85 MAX_SIMILAR=5 ./word2vectranslator ~/MODELS/en/cc.en.300.vec ./word2vec.model z.txt zo.txt

5. Проверьте качество моделей

Также стоит проверить, что русская модель действительно хорошего качества. Может быть полезно:
pascal

// Добавьте в конструктор TWordEmbeddings проверку
Writeln('Первые 10 слов в модели:');
for i := 0 to Min(9, FVocab.Count - 1) do
  Writeln('  ', FVocab[i]);

Это поможет понять, что содержится в модели и насколько она подходит для перевода.

Попробуйте эти изменения - они должны значительно улучшить качество перевода, фильтруя неподходящие кандидаты и повышая требования к схожести.