Я задал модели вопрос, на который она отвечала правильно ещё вчера, и получил вот это:
зцжме?ьещцжжщьжянбе?мгф:зацхнзахлкф:
Файл тот же самый. Код тот же самый, до буквы. Я не трогал ни одной строки. Разница только в том, какие числа лежат в двоичном макете внутри обработки.
Сразу оговорюсь, чтобы дальше не путаться. Это не обращение к ChatGPT и не коннектор к чужому серверу. Никакого ключа, никакого интернета, никакого облака: модель целиком лежит внутри файла обработки и считается процессором вашей машины. Материалов про подключение 1С к готовому API на площадке хватает, а тут ровно наоборот - смотрим, что у этой штуки внутри, когда она вся твоя.
Это снимок тех самых чисел, которые модель настраивает при обучении, взятый на нулевом шаге, то есть до того, как она чему-либо научилась. А дальше у меня есть ещё одиннадцать снимков, снятых по дороге. Один и тот же вопрос, одна и та же арифметика, двенадцать разных наборов чисел. И между первым и последним есть место, где происходит самое интересное в этой истории.
Что вообще меняется при обучении
Сначала договоримся о словах, потому что дальше без них никак.
Веса это числа. Просто числа, лежащие таблицей. В нашей обработке они хранятся двоичным макетом, и вся модель это 21 984 числа, ужатых до одного байта каждое. Двадцать два килобайта чисел, с заголовком и алфавитом файл выходит на двадцать четыре. Меньше, чем средняя печатная форма.
Устройство у неё простое до неприличия: два слоя, две головы внимания, алфавит на 37 символов. Всё, что она видела в жизни, это сорок пар вопрос-ответ.
Шаг обучения это один проход: показали модели кусок обучающего текста, посмотрели, насколько она промахнулась с предсказанием следующего символа, чуть-чуть подвинули числа в сторону меньшего промаха. Один шаг двигает числа на копейки. Шагов в этом прогоне двенадцать тысяч.
Потеря это и есть мера промаха. Чем меньше, тем точнее модель угадывает следующий символ обучающего текста. Нуля там не бывает никогда, потому что она не выбирает из готового списка, а раздаёт вероятности всем символам сразу.
И вот главное, что меня самого зацепило, когда я разложил снимки рядом. Модель при обучении не растёт. Двадцать четыре килобайта на нулевом шаге, двадцать четыре килобайта на двенадцатитысячном. Ни одной новой таблицы и ни одного нового Если. Меняются только значения в клетках, которые были там с самого начала.
Обучение это подбор коэффициентов, как если бы вы взяли готовый алгоритм расчёта себестоимости и двенадцать тысяч раз подкручивали в нём проценты, пока цифры не сойдутся.
Двенадцать снимков одного и того же вопроса
Учил я её на сорока парах вида "как найти элемент по коду" и ответ. Ответом всегда служило имя из платформы: найтипокоду, срезпоследних, начатьтранзакцию. Всё строчными буквами, потому что заглавные удвоили бы алфавит, а на такой крошечной модели каждый символ дорог.
Дальше я брал один и тот же вопрос из обучающего набора и задавал его весам каждого шага по очереди. Первые четыре строки нечитаемы, и это не опечатка вёрстки, а честный вывод модели. Смотреть там надо не на слова, а на то, как меняется их вид.
| Шаг | Потеря | Ответ на "как найти элемент по коду" |
|---|---|---|
| 0 | обучение не начиналось | зцжме?ьещцжжщьжянбе?мгф:зацхнзахлкф: |
| 50 | 2,31 | по: какакать кать к к к какакать как посокак |
| 200 | 1,74 | побратататать ратакументатататрататантарат |
| 350 | 1,21 | станикументала оватьтранзакцию? |
| 500 | 0,76 | регистьстрезапроса? |
| 800 | 0,29 | датановитьпометр. |
| 1 200 | 0,16 | справочники номенклатура выбрать. |
| 4 000 | 0,08 | справочники номенклатура найтипонаименованию. |
| 12 000 | 0,05 | справочники номенклатура найтипокоду. |
Прочитайте эту таблицу сверху вниз ещё раз, не торопясь. Там видно, в каком порядке модель что осваивает, и порядок этот совершенно не тот, который я ожидал увидеть.
Сначала появляются русские слоги. К пятидесятому шагу она уже не сыплет случайными буквами, а выдаёт что-то произносимое: "какакать", "посокак". Смысла ноль, но это уже похоже на язык.
К двухсотому проступают куски знакомых слов: "ратакументат" это раскрошенный "документ". К трёхсот пятидесятому из каши вылезает почти целое имя платформы: "оватьтранзакцию" это "начатьтранзакцию", у которого потеряно начало.
И только потом, сильно позже, приходит соответствие вопроса ответу.
Форма выучивается раньше содержания. Модель научилась звучать как 1С-ник задолго до того, как научилась отвечать правильно.
Шаг 800, на котором рождается враньё
Теперь посмотрите на две строки в середине таблицы.
шаг 500 регистьстрезапроса? шаг 800 датановитьпометр.
Вот здесь я, честно говоря, засмеялся, когда увидел это в первый раз. Так выглядят методы, которых в платформе нет, но которые вполне могли бы там быть.
"Датановитьпометр" склеен из настоящих кусков: дата, установитьпометкуудаления, параметр. Модель взяла реальные фрагменты, которые видела в обучающем тексте, и собрала из них то, чего не существует. Причём собрала уверенно, с точкой на конце, законченным утверждением.
И это ровно та болезнь, за которую ругают большие модели. Когда вам выдают метод, которого нет в синтакс-помощнике, происходит то же самое, что на восьмисотом шаге у моей игрушки на двадцать четыре килобайта. Разница в масштабе: у неё шов виден невооружённым глазом, у взрослой модели он спрятан за гладкой речью.
Отдельно проверил, что будет, если спросить о том, чего в обучающем наборе не было вовсе. Взял вопрос "как получить остатки номенклатуры" при том, что учил я её на "как получить остатки регистра". Полностью обученная модель, двенадцать тысяч шагов, ответила так:
справочники номенклатура найтипонаименованию.
Похоже, она зацепилась за слово "номенклатура" и выдала ответ про справочник вместо регистра. Не поручусь: этот же ответ она выдаёт на четырёхтысячном шаге и на вопрос из набора, так что, возможно, это её любимая фраза, а не рассуждение. Но по форме ответ безупречный, а по содержанию мимо.
Отказываться она не умеет. И дело не в том, что её не научили говорить "не знаю". В ней физически нет места, где хранилось бы знание о том, чего она не знает. Есть таблица чисел и правило выбора самого вероятного символа.
Про уверенность стоит сказать точнее, потому что дальше это ключевое. На каждый символ алфавита модель выдаёт по числу, все эти числа делятся на их же сумму и превращаются в проценты. Самый большой процент и есть ответ. Вот это деление и называют уверенностью модели. Оно ничего не знает про правильность, оно знает только про то, какие числа лежат в таблице.
Почему имена даются тяжелее определений
Тот же опыт я прогнал на другом наборе, и разница вышла поучительная.
В первом наборе ответом было определение: спрашиваешь "что такое регистр накопления", получаешь "регистр накопления хранит движения и итоги". Во втором ответом было имя из платформы, то есть тот набор, таблицу по которому вы видели выше.
На определениях модель управилась к двум тысячам шагов. Дальше потеря продолжала падать, а ответы уже не менялись ни на символ.
На именах ей понадобились все двенадцать тысяч. На четырёхтысячном и на восьмитысячном шаге она на вопрос "по коду" отвечала "найтипонаименованию", цепляясь за соседнюю пару из набора, и правильное имя поставила только на последнем снимке.
Разница ровно в одном. Определение наполовину лежит в самом вопросе: слова "регистр накопления" есть и в вопросе, и в ответе, модели достаточно научиться их переставлять. А имя метода из вопроса не выводится никак, его надо запомнить целиком, и запоминать его приходится по буквам, потому что модель работает посимвольно.
Отсюда следует вещь, которая мне кажется полезнее всего остального в этой статье. Модель уверенно ответит там, где ответ можно достроить из вопроса, и начнёт сочинять там, где нужно точно помнить. Имена типовых методов платформы большая модель видела тысячи раз, они у неё лежат крепко. А имена процедур из вашей конфигурации она не видела никогда.
Получается, врать про ваш код она будет увереннее всего именно потому, что вашего кода в ней нет. Форму она выдержит: ваш стиль именования и ваши сокращения. Содержание придумает.
Чего у меня не вышло
Задумывал я это иначе. Хотелось сделать обучение прямо внутри обработки: нажал кнопку, посидел, посмотрел, как модель учится у тебя на глазах. Красиво же.
Сел считать, и затея развалилась.
Один шаг обучения это прогон пачки из тридцати двух примеров по девяносто шесть символов контекста каждый, то есть 3 072 позиции за раз. На каждую позицию модель делает около 17,5 тысячи умножений. И всё это в обе стороны: сначала посчитать ответ, потом посчитать, куда двигать каждое из чисел, а обратный ход стоит примерно вдвое дороже прямого. Перемножаем: около 162 миллионов умножений на один шаг.
Скорость счёта на встроенном языке я замерял отдельно: примерно 440 тысяч умножений в секунду при выключенной отладке на сервере. Делим.
Шесть минут на один шаг. Пятьдесят одни сутки на двенадцать тысяч шагов.
Обидно тут вот что: отвечать она успевает вполне сносно. Один ответ длиной около сорока символов стоит примерно 1,4 миллиона умножений, и на моём ноутбуке это чуть больше трёх секунд при выключенной отладке. Медленно по меркам чего угодно, но сидеть и ждать можно.
Кстати, про отладку. Тот же ответ я померил при включённой и выключенной отладке на сервере, и разница вышла почти двукратной: 7,3 секунды против 3,8. Это на первой версии цикла, где тело умножения занимало три строки. После того как я склеил его в одну по подсказке из комментариев к первой части, стало 4,7 против 3,2, и разрыв упал до полутора раз. Отладчик берёт свою плату с каждой строки внутри цикла, а цикл умножения выполняется миллионы раз. Если будете мерить скорость своего кода, сначала выключите отладку, иначе померите отладчик.
Те же двенадцать тысяч шагов на обычном ноутбуке средствами Python заняли четыре минуты. Разница почти в восемнадцать тысяч раз, но честно будет сказать, чем она набрана. Python здесь считает не сам, он зовёт библиотеку на C, которая перемножает матрицы целиком и раскидывает работу по ядрам процессора. Так что это не соревнование двух языков, а скалярный цикл против машины, заточенной ровно под эту арифметику. У встроенного языка такой библиотеки нет, и в этом всё дело.
Обучение осталось снаружи, а внутрь обработки поехали готовые снимки весов. Считать ответ на встроенном языке можно, обучать нельзя, и упирается всё в арифметику, идейных препятствий тут никаких.
Как проверить, что я тут ничего не подрисовал
Вопрос законный, я бы сам его задал. Три способа, все доступны без моего участия.
Первое. Веса лежат макетами внутри файла, код открыт, и вся модель со всей обвязкой это около шестисот строк на встроенном языке. Точка останова внутри функции, которая перемножает вектор на матрицу, покажет вам про эту арифметику больше, чем ещё десяток моих абзацев.
Второе, и оно мне самому пригодилось. Обучение шло на Python, а считает ответ обработка на встроенном языке. Это две разные реализации одной и той же арифметики, и я сверил их ответы посимвольно. На десяти снимках из двенадцати текст совпал буква в букву.
А на двух разошёлся, на сотом шаге и на трёхсот пятидесятом, и вот это оказалось полезнее совпадений. Причина в том, что числа в макете ужаты до одного байта. Там, где модель уверена в следующем символе, потеря точности ничего не меняет. Там, где два варианта почти равны, она переворачивает выбор. То есть расхождение двух реализаций само по себе показывает, где модель твёрдо знает ответ, а где гадает между близкими вариантами.
Третье. Журнал обучения с потерями и ответами на каждом шаге лежит рядом с исходниками, и рядом же протокол прогона самой обработки, по которому эти десять из двенадцати и посчитаны. Прогон воспроизводится одной командой, зерно случайности зафиксировано.
И честная оговорка про масштаб опыта, без неё всё вышесказанное стоит меньше. Сорок пар, пачка по тридцать два, двенадцать тысяч шагов это девять тысяч шестьсот проходов по всему набору. Модель его именно зазубрила. Отложенной выборки у меня нет, все ответы в таблице на вопрос из обучающего набора, и потеря 0,05 в конце означает "выучила сорок строк наизусть", а не "поняла язык". Контрольных вопросов три. Порядок освоения, который видно в таблице, я на этом вижу отчётливо, но утверждать, что на большой модели он такой же, мне не на чем.
Сама обработка со всеми двенадцатью снимками лежит отдельной публикацией, ссылка в конце. Здесь я про неё намеренно не рассказываю, чтобы не превращать разбор в инструкцию.
Чего я так и не понял
Есть в этой таблице место, которое меня до сих пор смущает.
Между шагом 1 200 и шагом 12 000 потеря упала втрое: с 0,16 до 0,05. Десять тысяч восемьсот шагов работы, девять десятых всего обучения. А ответ на вопрос из обучающего набора за это время поменялся дважды, и то по кругу: сначала на соседний неправильный вариант, потом на верный.
Получается, подавляющая часть обучения ушла на что-то, чего я в ответах не вижу. Числа продолжали двигаться, потеря продолжала падать, а наружу это почти не выходило. У меня есть догадка, что модель в это время дошлифовывала редкие сочетания символов, которые на моих трёх контрольных вопросах просто не встречаются. Проверить её я пока не придумал как.
И вопрос, ради которого я всё это, собственно, и раскладывал. Если уверенность модели это результат деления, а не знания, то по какому признаку вы отличаете верный ответ большой модели от неверного, когда сами ответа не знаете? У меня рабочего признака нет, кроме одного: проверять всё, что она называет по имени. Если у вас есть другой, расскажите, мне правда интересно.
Обработка со всеми двенадцатью стадиями обучения - Обучение на глазах: веса модели на разных шагах обучения, бесплатно, код открыт.
Первая часть, где разбирается сама арифметика ответа: Внутри LLM нет ни одного Если. Языковая модель целиком на встроенном языке 1С и обработка к ней Локальная LLM на встроенном языке 1С.
Вступайте в нашу телеграмм-группу Инфостарт