Форум программистов, компьютерный форум, киберфорум
ИИ, нейросети, LLM, ML, Data Science, ИИ-агенты
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Эксперт функциональных языков программированияЭксперт по математике/физике
4313 / 2105 / 431
Регистрация: 19.07.2009
Сообщений: 3,204
Записей в блоге: 24

Как составлять обучающую выборку? (на примере поддержки малых языков)

26.12.2025, 23:38. Показов 685. Ответов 8

Студворк — интернет-сервис помощи студентам
Общий вопрос, вызывающий у меня полное непонимание, — как составлять обучающую выборку, чтобы обучение было эффективным и результат подходящим.
Всё ниже — это пример задачи, повод для обсуждения.

Рассмотрим задачу:
Дан малый язык программирования. Реализовать ассистента, который помогает пользователю быстро найти ответы на следующие вопросы:
  1. (Прямая задача) Что делает некоторый код?
    • Необходимо объяснить синтаксис и как отдельные части друг с другом сочетаются тем, кто впервые видит этот код и этот язык.
  2. (Обратная задача) Как реализовать некоторую задачу в виде кода?
    • Необходимо написать синтаксически корректный код по точному строгому словесному описанию от программиста, который знает, что он хочет получить, но не знает этот язык. Предметно рассуждать не требуется.
В качестве примера предлагаю взять jq — это достаточно известная программа, но всё ещё имеющая все признаки малого языка.

Будем для простоты рассматривать вызов jq без ключей. В таком режиме jq преобразует входной поток (последовательность) JSON-объектов в выходной поток JSON-объектов.

Напомним синтаксис
Можно ограничиться фрагментом синтаксиса команд:
1. . — выходной поток совпадает со входным;
2. query1 | query2 — композиция: сначала входной поток преобразуется по query1, а затем — по query2;
3. .[key] — у каждого объекта из входного потока извлекается значение свойства key (если строка — то свойства объекта, если число — элемент массива по индексу); если свойство отсутствует, возвращается null;
4. .foo является синонимом .["foo"], .foo.bar эквивалентно .foo | .bar;
5. select(query) — для каждого объекта x из входного потока запустить query и вывести в выходной поток столько копий x, сколько истинозначных (не null или false) значений выдало query

Например,
Code
1
jq 'select(.isActive) | .username | select(.)'
Из входного потока удалить все объекты с ложным (или отсутствующим) свойством isActive, у оставшихся взять свойство username и оставить только истинозначные значения.


Если бы была только прямая задача, я бы её решал следующим образом:
1. алгоритмическое текстовое описание запроса сухим языком, но точно соответствуя синтаксису и семантике конструкций;
2. полировка описания языковой моделькой.

Я вижу такой метод решения прямой и обратной задачи:
Берём модель общего назначения и дообучаем её низкоранговой Лорой на обучающей выборкой.

Как сформировать обучающую выборку? Очевидно, автоматически. Возможно, с последующей ручной правкой.

Какие шаблоны вопросов и шаблоны ответов использовать?
Как много пар вопрос-ответ генерировать?
В каком распределении генерировать запросы? Сколько простых запросов? Сколько запросов длины 10, 20, 50?

Я могу сгенерировать обучающую выборку любого размера любого содержания. По каким критериям мне определять хорошесть той или иной выборки?
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
26.12.2025, 23:38
Ответы с готовыми решениями:

Как создать качественную обучающую выборку?
Здравствуйте! Имеются исторические данные (генеральная совокупность), необходимо по этим данным...

Как создать несложную обучающую выборку для нейронной сети?
Где можно взять данные для выборки?

Как правильно обучать multilabel классификатор на данных вне обучающей выборки (пустому/ненужному классу)?
Подскажите пожалуйста, как поступать когда обучаем multilabel классификатор (метки...

8
Эксперт функциональных языков программированияЭксперт по математике/физике
4313 / 2105 / 431
Регистрация: 19.07.2009
Сообщений: 3,204
Записей в блоге: 24
04.01.2026, 15:42  [ТС]
Вопрос ещё открыт.
И для того, чтобы повысить конструктивность, предлагаю конкретный набор данных для обучающей выборки.
Пример 10 пар вопрос-ответ

Q: How do I write an object of the input stream, which is not false nor null in jq?
A: So, the output stream must consist of an object of the input stream, which is not false nor null.
It can be represented with the jq expression
```jq
select(.)
```

The expression `select(.)` returns only those objects of the input stream, which is not false nor null.

Q: How do I write a property "code" of input object's property "fullyInsteadPrefixLogic" in jq?
A: So, the output stream must consist of a property "code" of input object's property "fullyInsteadPrefixLogic".
It can be represented with the jq expression
```jq
.fullyInsteadPrefixLogic | .code
```

The expression `.fullyInsteadPrefixLogic | .code` represents a composition of two transformations, `.fullyInsteadPrefixLogic` and `.code`.
- The expression `.fullyInsteadPrefixLogic` means that the output stream is of the same length as the input stream and it consists of values of properties "fullyInsteadPrefixLogic" (null if no property is found or an input object is a number, a string or null).
- The expression `.code` returns a value of the property "code" (null if no property is found or an input object is a number, a string or null).

Q: Explain the jq expression `.`
A: The expression `.` acts as an identity function which moves an input object to the output "as is" with no action performed.

Q: Explain . in jq
A: The expression `.` means the input object itself with no modification.

Q: What does the expression `select(.seekingMethodsMothers | .evolutionTrail) | select(.fo) | .garbageSoulsEminem | select(.suffered | .[0] | .entCrazyDelayed | .[13] | .intDollarsSoulsOnion | .ethernetMpFolder | .supplier | .advertThongsRachel | .mindsPermit | .modelPretty | .suseTent | .gnomeThemes | .wrapping | .bookArcCourier)` stand for in jq?
A: The jq expression `select(.seekingMethodsMothers | .evolutionTrail) | select(.fo) | .garbageSoulsEminem | select(.suffered | .[0] | .entCrazyDelayed | .[13] | .intDollarsSoulsOnion | .ethernetMpFolder | .supplier | .advertThongsRachel | .mindsPermit | .modelPretty | .suseTent | .gnomeThemes | .wrapping | .bookArcCourier)` represents a property "garbageSoulsEminem" of an object of the input stream, a property "evolutionTrail" of whose property "seekingMethodsMothers" is not false nor null, a property "fo" of which is not false nor null, a property "bookArcCourier" of a property "wrapping" of a property "gnomeThemes" of a property "suseTent" of a property "modelPretty" of a property "mindsPermit" of a property "advertThongsRachel" of a property "supplier" of a property "ethernetMpFolder" of a property "intDollarsSoulsOnion" of an element #13 of a property "entCrazyDelayed" of an element #0 of whose property "suffered" is not false nor null,.

Q: In jq, what is .geography?
A: The expression `.geography` means that the output stream is of the same length as the input stream and it consists of values of properties "geography" (null if no property is found or an input object is a number, a string or null).

Q: What jq expression shall I use to represent a property "big" of a property "coverAgoFinalMed" of an object of the input stream, a property "toysNaked" of a property "2\\Hva^." of a property "vp" of a property "republicVgKirkMail" of whose property "quilt" is not false nor null, which is not false nor null?
A: So, the output stream must consist of a property "big" of a property "coverAgoFinalMed" of an object of the input stream, a property "toysNaked" of a property "2\\Hva^." of a property "vp" of a property "republicVgKirkMail" of whose property "quilt" is not false nor null, which is not false nor null.
It can be represented with the jq expression
```jq
select(.quilt | .republicVgKirkMail | .vp | .["2\\Hva^."] | .toysNaked) | .coverAgoFinalMed | .big | select(.)
```

The expression `select(.quilt | .republicVgKirkMail | .vp | .["2\\Hva^."] | .toysNaked) | .coverAgoFinalMed | .big | select(.)` transforms the input stream sequentially by applying `select(.quilt | .republicVgKirkMail | .vp | .["2\\Hva^."] | .toysNaked) | .coverAgoFinalMed | .big` first and then `select(.)`.
- The expression `select(.quilt | .republicVgKirkMail | .vp | .["2\\Hva^."] | .toysNaked) | .coverAgoFinalMed | .big` represents a composition of two transformations, `select(.quilt | .republicVgKirkMail | .vp | .["2\\Hva^."] | .toysNaked)` and `.coverAgoFinalMed | .big`.
- The expression `select(.quilt | .republicVgKirkMail | .vp | .["2\\Hva^."] | .toysNaked)` represents a filtering function with a predicate defined by the query `.quilt | .republicVgKirkMail | .vp | .["2\\Hva^."] | .toysNaked`:
- The expression `.quilt | .republicVgKirkMail | .vp | .["2\\Hva^."] | .toysNaked` represents a composition of two transformations, `.quilt | .republicVgKirkMail | .vp | .["2\\Hva^."]` and `.toysNaked`.
- The expression `.quilt | .republicVgKirkMail | .vp | .["2\\Hva^."]` is a composition of `.quilt | .republicVgKirkMail | .vp` and `.["2\\Hva^."]`.
- The expression `.quilt | .republicVgKirkMail | .vp` represents a property "vp" of a property "republicVgKirkMail" of an object's property "quilt".
- The expression `.["2\\Hva^."]` means that the output stream is of the same length as the input stream, each input object produces a value of its property "2\\Hva^." if defined, and null otherwise.
- The expression `.toysNaked` means that the output stream is of the same length as the input stream, each input object produces a value of its property "toysNaked" if defined, and null otherwise.
- The expression `.coverAgoFinalMed | .big` transforms the input stream sequentially by applying `.coverAgoFinalMed` first and then `.big`.
- The expression `.coverAgoFinalMed` means that the output stream is of the same length as the input stream, each input object produces a value of its property "coverAgoFinalMed" if defined, and null otherwise.
- The expression `.big` means that the output stream is of the same length as the input stream and it consists of values of properties "big" (null if no property is found or an input object is a number, a string or null).
- The expression `select(.)` picks those objects of the input stream that satisfy the query `.`.

Q: What does the expression `.` stand for in jq?
A: The expression `.` acts as an identity function which moves an input object to the output "as is" with no action performed.

Q: What does the expression `.` mean in jq?
A: The jq expression `.` represents an object of the input stream.

Q: What jq expression shall I use to represent an object of the input stream?
A: So, the output stream must consist of an object of the input stream.
It can be represented with the jq expression
```jq
.
```

The expression `.` means the input object itself with no modification.


В приложении находится образец на 10 000 пар вопрос-ответ в формате JSON, совместимым с SFTTrainer и другими аналогичными инструментами.

Пример обучения с этими данными

Предполагается, что файл train.json из приложения лежит в папке /workdir/dataset.
Файл test.json я не прикрепляю, но он у меня выглядит аналогично train.json, только меньше в 100 раз.

И ещё ремарка: мне пришлось создать файл /workdir/template.jinja с шаблоном запроса:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{% for message in messages %}
<|im_start|>{{ message['role'] }}\n
{% if message['content'] is string %}
{% if message['role'] == 'assistant' %}
{% generation %}
{{ message['content'] }}
{% endgeneration %}
{% else %}
{{ message['content'] }}
{% endif %}
{% endif %}
<|im_end|>\n
{% endfor %}
{% if add_generation_prompt %}
<|im_start|>assistant\n
{% endif %}
В теории, можно без него, но я не разобрался, как это сделать.

Исполняемый модуль:
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer
import accelerate
from peft import LoraConfig, TaskType, get_peft_model
from datasets import load_dataset
from trl import SFTConfig, SFTTrainer
 
dataset = load_dataset('json', data_files = {
    "train": "/workdir/dataset/train.json",
    "test": "/workdir/dataset/test.json"
})
 
workdir = "/workdir/"
model_id = "Qwen/Qwen2.5-0.5B-Instruct"
model = AutoModelForCausalLM.from_pretrained(model_id, device_map="cpu")
tokenizer = AutoTokenizer.from_pretrained(model_id, use_fast=True)
 
# see https://kickitlikeshika.github.io/2024/07/24/how-to-fine-tune-llama-3-models-with-LoRA.html
PAD_TOKEN = "<|pad|>"
tokenizer.add_special_tokens({ "pad_token": PAD_TOKEN })
tokenizer.padding_side = "right"
# we added a new padding token to the tokenizer, we have to extend the embddings
 
peft_config = LoraConfig(
    r=4,
    lora_alpha=16,
    task_type=TaskType.CAUSAL_LM,
    target_modules=["q_proj", "v_proj"],
)
model = get_peft_model(model, peft_config)
 
accelerate.hooks.remove_hook_from_module(model, recurse=True)
 
sft_config = SFTConfig(
    output_dir="/workdir/out-c",
    use_cpu=True,
    use_mps_device=False,
    assistant_only_loss=True,
    max_length=64, # number of tokens in one batch. If packing is off, all the rest tokens long messages in the dataset will be ignored.
    # packing=True, # no need in packing if the dataset's items are of the same length
    num_train_epochs=1,
    per_device_train_batch_size=2,  # training batch size
    #per_device_eval_batch_size=2,  # eval batch size
    gradient_accumulation_steps=8,  # by using gradient accum, we updating weights every: batch_size * gradient_accum_steps = 4 * 4 = 16 steps
    eval_strategy='steps',
    eval_steps=5,
    save_steps=5,
    learning_rate=5e-4,
    save_strategy='steps',
    save_safetensors=True,
    dataset_kwargs={
        "add_special_tokens": False,  # we template with special tokens already
        "append_concat_token": False,  # no need to add additional sep token
    },
    chat_template_path="/workdir/template.jinja",
    seed=1,
    logging_dir="/workdir/logs",
    logging_steps=1,
)
 
trainer = SFTTrainer(
    model=model,
    args=sft_config,
    train_dataset=dataset["train"],
    eval_dataset=dataset["test"],
)
 
trainer.train()
 
model.save_pretrained("/workdir/out")


Первыми результатами похвастаться не могу.

Вопрос прежний: можно ли на этих данных натренировать сеть, скажем, Qwen/Qwen2.5-0.5B-Instruct (вроде подходящая низкопараметричная модель в качестве базовая), чтобы она была полезным помощником?
Следует ли улучшить качество обучающих данных?
Какой объём данных и сколько эпох использовать?
Надо ли повышать или понижать долю вопросов на select, например, или на любую другую синтаксическую конструкцию?
Вложения
Тип файла: zip train.json.zip (436.5 Кб, 6 просмотров)
0
wound up as Aussie
462 / 114 / 20
Регистрация: 15.05.2019
Сообщений: 453
07.01.2026, 17:06
Понимаете, это очень сложный вопрос, хотя... В общем, дело обстоит так:
- Надо набрать набор неправильных слов (по сути наборы букв или/и ошибки в словах).
И чем больше, тем лучше, но привязанных к Вашему словарю - ну как-то так.
Я могу скинуть Вам свою разработку на java (пришлось обращаться к ИИ, т.к. там очень много того, что связано со звуком, пришлось дообучаться (пытаться это сделать)). Я скину с комментариями (моими и ИИ).

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package smallasr_added;
 
 
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
 
/**
 * ProChatLSTM.java
 * Чистая Java. LSTM чат с:
 *  - загрузкой/сохранением датасета (CSV "q,a"), экспорт/импорт
 *  - continue training (дообучение)
 *  - конфигом (chat.cfg) и интерактивным меню
 *  - режимы: WORD + CHAR; word-режим имеет char-fallback при OOV
 *  - генерация: greedy / temperature / top-k / top-p / beam(+length penalty,+temperature)
 *
 * Файлы по умолчанию:
 *  chat.cfg, data.csv, vocab_word.txt, vocab_char.txt, lstm_word.bin, lstm_char.bin
 * 
 * Thought for 9s
 Ниже — расширенная версия на чистой Java:
сохранение/загрузка датасета (CSV) и дообучение (continue training),
конфиг в chat.cfg (Properties) + интерактивное меню для правки,
char-fallback в word-режиме (если много OOV — генерим ответ char-LSTM’ом),
экспорт/импорт CSV Q→A,
beam search с length penalty и temperature для beam.
Собирай как обычно: javac ProChatLSTM.java → java ProChatLSTM
По умолчанию создаст/прочитает chat.cfg, data.csv, vocab_word.txt, vocab_char.txt, 
* lstm_word.bin, lstm_char.bin.
* 
* *********************************
* Вот (если надо), следующий апгрейд:
сохранение/загрузка чекпоинтов по эпохам,
авто-оценка перплексии,
мини-батчи с усреднением потерь и прогресс-баром,
JSON-конфиг + профили (dev, train, inference),
упаковка в jar и опции командной строки (--train --epochs 500 ...).
* 
* ***********************************************************************
* Как расширить дата set (или что сделать чтобы она правильно отвечала) ?
 
 
Это нормально для старта: данных мало, поэтому модель «угадывает». Чтобы начала отвечать по делу, нужно прокачать и данные, 
* и режим генерации. Вот что надо, по шагам.
 
1) Быстрые настройки инференса
Попробуй beam search: в конфиге chat.cfg поставь
gen.mode=5, gen.beam.size=5, gen.beam.temp=0.9..1.0, gen.beam.lenpen=0.6.
Это уменьшит рандом и сократит «мусор».
Для сэмплинга: temperature 0.7–0.9, top-k=5–20. Чем ниже — тем более «сдержанно».
 
2) Главный рычаг — датасет
Сейчас датасет игрушечный. Нужны интенты и вариативность формулировок.
 
Мини-план:
Составь список 15–30 частых тем/интентов (приветствие, «кто ты», «что такое RNN/LSTM/AI», «как тебя звать», 
* «чем можешь помочь», «спасибо», «погода? → не знаю», «переведи слово», и т.п.).
Для каждого интента добавь 10–20 вариантов вопроса (перефразирования, рус/англ, с опечатками).
Дай 2–5 вариантов ответа на интент (коротких и длиннее).
Итого получится хотя бы ~300–600 пар Q→A. Уже будет заметно лучше.
* 
Пример фрагмента data.csv (ты можешь занести это через пункт меню «Import CSV»):
 
q,a
hello,hi
hi,hello
hey,hi
привет,привет
как тебя зовут,я простая l s t m
кто ты,я простая l s t m
что такое rnn,r n n is a network with memory
что такое lstm,l s t m is a network with long memory
что такое ai,ai is artificial intelligence
спасибо,пожалуйста
благодарю,пожалуйста
чем можешь помочь,i can chat a little
what can you do,i can chat a little
кто твой автор,i am a simple model written in java
не понял,hmm... can you rephrase?
Добавляй побольше перефразировок одного смысла — сеть начнёт «схватывать» намерение.
 
3) Как правильно дообучать в этой программе
Собери свой CSV (как выше) → через меню 6) Import CSV добавь в текущий data.csv.
Заново построй словари (программа сделает это сама при импорте) и дообучи:
 
«3) Continue training (word)»
при большом количестве опечаток или смешанных языков — «4) Continue training (char)» (символьная модель более устойчива).
После обучения 9) Save vocabs + models.
Советы:
Лучше сначала собрать полный датасет, потом тренировать с нуля (иначе при росте словаря модель переинициализируется).
В chat.cfg увеличь:
epochs=400 (или 600)
hidden.word=192 или 256 (если CPU тянет)
microbatch=8
При долгой тренировке можно снизить lr до 0.04.
* 
4) Баланс генерации и данных
Если ответы хорошие по смыслу, но «болтает» — снижай температуру/увеличивай beam.
Если модель «молчит» или частит <unk> — расширяй словарь (больше данных) и проверяй oov.threshold 
* (например, 0.25) — это порог включения char-fallback.
 
5) Делай «защитные» интенты
Добавь намеренно пары на то, чего модель не знает:
q,a
какая сейчас погода,я не знаю погоду. я оффлайн чат-модель.
сколько сейчас времени,я не знаю текущее время. я оффлайн.
переведи на китайский,я не умею переводить на китайский в этой версии.
Так ты избежишь галлюцинаций — сеть будет отвечать честно.
 
6) Контекст диалога (опционально)
Наш код сейчас не хранит историю. Быстрый хак: конкатенируй 1–2 предыдущие реплики к текущему вопросу при кодировании (например, prev_user + " || " + prev_bot + " || " + now_user"). Если нужно — скажи, пришлю маленький патч, который будет держать «скользящее окно» контекста.
 
7) Проверка качества
Отложи 10–20% пар как dev (не обучай на них). Периодически проверяй ответы на dev — ты увидишь, улучшается ли обобщение.
Смотри частые ошибки: где путает интенты — добивай датасет целевыми примерами.
 
8) Когда использовать char-режим
Много опечаток, смешение RU/EN, редкие термины — char-LSTM поможет. Он медленнее, но устойчивее.
В конфиге можно снизить gen.temperature и для char, чтобы не «растекалась».
* ******************
* Хочешь — дам шаблон CSV с ~120 базовыми парами (RU+EN) для старта и короткий патч, 
* который добавит «окно истории» из последних двух реплик в chatLoop.
* *************************** Давай... 
* 
 */
Добавлено через 3 минуты
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
public class ProChatLSTM {
 
    /* ======================= DATA ======================= */
    static class Pair { final String q,a; Pair(String q,String a){this.q=q; this.a=a;} }
 
    /* ===================== TOKENIZER ==================== */
    enum Mode { WORD, CHAR }
 
    static class Vocab {
        final Mode mode;
        Map<String,Integer> w2i = new HashMap<>();
        List<String> i2w = new ArrayList<>();
        int PAD, BOS, EOS, UNK;
 
        Vocab(Mode m){
            mode=m;
            PAD=add("<pad>");
            BOS=add("<bos>");
            EOS=add("<eos>");
            UNK=add("<unk>");
        }
        int add(String w){ if(!w2i.containsKey(w)){int id=i2w.size(); w2i.put(w,id); i2w.add(w);} return w2i.get(w); }
        int get(String w){ return w2i.getOrDefault(w, UNK); }
        String get(int id){ return (id>=0&&id<i2w.size())? i2w.get(id): "<unk>"; }
        int size(){ return i2w.size(); }
 
        String[] tokenize(String s){
            if (s==null) return new String[0];
            s = s.toLowerCase();
            if (mode==Mode.WORD){
                s = s.replaceAll("[^a-z0-9а-яё\\-\\' ]"," ")
                     .replaceAll("\\s+"," ").trim();
                if (s.isEmpty()) return new String[0];
                return s.split("\\s+");
            } else {
                s = s.replaceAll("[\\r\\n\\t]"," ").trim();
                if (s.isEmpty()) return new String[0];
                String[] arr = new String[s.length()];
                for (int i=0;i<s.length();i++) arr[i]=String.valueOf(s.charAt(i));
                return arr;
            }
        }
        List<Integer> encodeExpand(String text){ // расширяем словарь
            List<Integer> ids=new ArrayList<>();
            ids.add(BOS);
            String[] toks = tokenize(text);
            for (String t: toks) add(t);
            for (String t: toks) ids.add(get(t));
            ids.add(EOS);
            return ids;
        }
        List<Integer> encodeKnown(String text){ // не расширяем
            List<Integer> ids=new ArrayList<>();
            ids.add(BOS);
            for (String t: tokenize(text)) ids.add(get(t));
            ids.add(EOS);
            return ids;
        }
        int countOOV(String text){
            int c=0; for (String t: tokenize(text)) if (!w2i.containsKey(t)) c++;
            return c;
        }
        String decode(List<Integer> ids){
            StringBuilder sb=new StringBuilder();
            for (int id: ids){
                if (id==BOS||id==EOS||id==PAD) continue;
                String t=get(id);
                if (mode==Mode.WORD){
                    if (sb.length()>0) sb.append(" ");
                    sb.append(t);
                } else sb.append(t);
            }
            return sb.toString().trim();
        }
        /* save/load vocab (plain text) */
        void save(String path) throws IOException {
            try (BufferedWriter bw = new BufferedWriter(new FileWriter(path))){
                bw.write(mode.name()); bw.newLine();
                for (String tok: i2w) { bw.write(tok); bw.newLine(); }
            }
        }
        static Vocab load(String path) throws IOException {
            try (BufferedReader br = new BufferedReader(new FileReader(path))){
                String mline = br.readLine();
                Mode m = Mode.valueOf(mline.trim());
                Vocab v = new Vocab(m);
                v.w2i.clear(); v.i2w.clear();
                String line;
                while((line=br.readLine())!=null){
                    String tok=line;
                    if(tok.isEmpty()) tok=" ";
                    int id=v.i2w.size();
                    v.i2w.add(tok); v.w2i.put(tok,id);
                }
                v.PAD=v.w2i.get("<pad>"); v.BOS=v.w2i.get("<bos>");
                v.EOS=v.w2i.get("<eos>"); v.UNK=v.w2i.get("<unk>");
                return v;
            }
        }
    }
 
    /* ====================== MATH ======================= */
    static class MU {
        static final Random rnd = new Random(42);
        static double[][] randn(int r,int c,double s){ double[][] M=new double[r][c];
            for(int i=0;i<r;i++) for(int j=0;j<c;j++) M[i][j]=(rnd.nextDouble()*2-1)*s; return M; }
        static double[] randn(int n,double s){ double[] v=new double[n];
            for(int i=0;i<n;i++) v[i]=(rnd.nextDouble()*2-1)*s; return v; }
        static double[][] zeros(int r,int c){ return new double[r][c]; }
        static double[] zeros(int n){ return new double[n]; }
        static double sigmoid(double x){ return 1.0/(1.0+Math.exp(-x)); }
        static double dsig_y(double y){ return y*(1.0-y); }
        static double tanh(double x){ return Math.tanh(x); }
        static double dtanh_y(double y){ return 1.0 - y*y; }
        static double[] softmax(double[] z){
            double m=Double.NEGATIVE_INFINITY; for(double v: z) m=Math.max(m,v);
            double s=0; double[] e=new double[z.length];
            for(int i=0;i<z.length;i++){ e[i]=Math.exp(z[i]-m); s+=e[i]; }
            for(int i=0;i<z.length;i++) e[i]/=s; return e;
        }
        static int argmax(double[] v){ int id=0; double b=v[0]; for(int i=1;i<v.length;i++) if(v[i]>b){b=v[i]; id=i;} return id; }
        static void clip(double[][] A,double thr){
            double n=0; for(double[] r: A) for(double x:r) n+=x*x; n=Math.sqrt(n);
            if(n>thr){ double k=thr/n; for(int i=0;i<A.length;i++) for(int j=0;j<A[i].length;j++) A[i][j]*=k; }
        }
        static void clip(double[] v,double thr){
            double n=0; for(double x:v) n+=x*x; n=Math.sqrt(n);
            if(n>thr){ double k=thr/n; for(int i=0;i<v.length;i++) v[i]*=k; }
        }
    }
0
wound up as Aussie
462 / 114 / 20
Регистрация: 15.05.2019
Сообщений: 453
07.01.2026, 17:11
Цитата Сообщение от Mysterious Light Посмотреть сообщение
Вопрос прежний
что-то запутался, очень длинный файл - слишком много всего...
Скидываю zip
Вложения
Тип файла: zip simpleLSTM.zip (12.8 Кб, 7 просмотров)
0
Эксперт функциональных языков программированияЭксперт по математике/физике
4313 / 2105 / 431
Регистрация: 19.07.2009
Сообщений: 3,204
Записей в блоге: 24
07.01.2026, 22:34  [ТС]
Честно говоря, я не понял ответ.

Выглядит это так, будто это описание задачи для кодогенерирующей модели и ответ от неё в виде кода.
Сама задача для кодогенерации выглядит как общее описание основных шагов обучения нейросети.

Сравнение этой задачи с моим вопросом:
1. Я интересуюсь только вопросом, как собирать данные для обучающей выборки. Остальные этапы (режим обучения, изменение словаря) тоже интересны, он не входят в текущий поставленный вопрос. Я хотел бы разделить их обсуждение, чтобы не сложилась каша в моей голове. В приведённом коде рассматриваются (причём поверхностно) все этапы.
2. Мой вопрос я ставлю на примере конкретной прикладной задачи: обучить ассистента, который 1) объяснял jq-запрос и 2) составлял jq-запрос по описанию на естественном языке. Даже язык выбрал (jq) и для него составил пары вопрос-ответ. В приведённом коде прикладная задача не понятна. Там что-то говорится про исправление ошибок, но я не увидел примеров пар вопрос-ответ (те, что есть, не объясняют принцип составления выборки и не масштабируются на больший объём).
3. Я привёл конкретный пример обучающей выборки. В приведённом коде обучающая выборка должна, видимо, сгенерироваться сторонней LLM. Оставляя в стороне вопрос о корректности составляемой таким образом выборки, я не вижу пример такой выборки. На чём предлагается учить модель?

И ещё в копилку вопросов: почему в приведённом коде смешиваются фазы обучение и использования (инференса) модели? Это же разные фазы, которые в общем случае могут выполняться на разных машинах.

Код я не осилил — он без комментариев, без внятной структуры и со странным форматированием. В любом случае, в нём не содержится ответ на мой первоначальный вопрос о критериях качества обучающей выборки.

Получается, вопрос открыт.
0
wound up as Aussie
462 / 114 / 20
Регистрация: 15.05.2019
Сообщений: 453
09.01.2026, 14:20
Цитата Сообщение от Mysterious Light Посмотреть сообщение
Python
1
2
3
max_length=64, # number of tokens in one batch. If packing is off, all the rest tokens long messages in the dataset will be ignored.
    # packing=True, # no need in packing if the dataset's items are of the same length
    num_train_epochs=1,
Вот здесь надо max_length сделать 256 (хотя бы) - может система даже не читает всего остального, и эпох надо хотя бы 2 или 3.
Сейчас "проконсультируюсь".... ))
1
wound up as Aussie
462 / 114 / 20
Регистрация: 15.05.2019
Сообщений: 453
09.01.2026, 14:43
Цитата Сообщение от Mysterious Light Посмотреть сообщение
о критериях качества обучающей выборки
Мдя... там в самом деле непросто...
Посмотрите в attachment (там я закинул, по-моему, всё что Вам будет нужно - надеюсь. Там ещё Ваш json подкорректирован).
Вложения
Тип файла: zip Explanation_jq&pyScript&train_v2.json.zip (474.5 Кб, 10 просмотров)
1
Эксперт функциональных языков программированияЭксперт по математике/физике
4313 / 2105 / 431
Регистрация: 19.07.2009
Сообщений: 3,204
Записей в блоге: 24
11.01.2026, 00:28  [ТС]
Мне понравился ответ. Прямо в восторге. Надо его, как минимум, выложить в виде сообщения в этой теме.
Как максимум — после окончания этого эксперимента оформить в виде замкнутой статьи.

Всё прочитал, но не всё ещё осмыслил и ввёл в употребление.
Я прокомментирую то, что есть на данный момент.

Анекдот про max_length

Поставил некогда max_length=64, когда самые первые короткие тексты были, а потом и забыл о нём.

Тренирую-тренирую свою модель и дивом дивлюсь: почему после первых двух-трёх предложений модель стабильно уходит в какую-то лютую галлюцинацию?

Перезапускал несколько раз, менял тексты — всё равно.

В итоге поставил в конце каждого ответа ✅, чтобы видеть, где по мнению обученной модели находится конец ответа и начало галлюцинации. Что интересно, даже так этот символ встречался крайне редко.

От таки дела, малята.


Я пришел к выводу, что случайным образом генерировать jq-выражения нерационально: слишком многие выражения лишены практического смысла.
Например, select(select(.foo)).

Поэтому сейчас я действую следующим образом:
1. сначала генерирую тип выходных данных (язык типов состоит из массива, объекта и 4 примитивных типов, даже альтернативу не использую) и выходных (только примитивный тип, вроде number, string, boolean или null)
2. затем генерирую jq-выражение, который типизируется этими двумя типами. Для этого я составил специализированную систему типов и правила вывода для них. Упрощает задачу то, что в выбранном подмножестве jq нет связанных переменных.
3. упрощаю (не обязательный этап) выражение: (q | .) → q и т.п.

Так получается гораздо более естественные jq-выражения.
Правда, пришлось расширить подмножество jq конструкциями empty, true, false и прочими константами, а также бинарными сравнениями x == y, x < y и т.п. Впрочем, так даже лучше выглядят и примеры, и объяснения.

Мне рекомендовали использовать fast-check (или любую другую библиотеку для property-based testing) для записи генераторов — вполне приятная альтернатива ручному управлению вероятностями, бонусом идёт воспроизводимость.

Текущие (на данный момент) открытые вопросы:
  • Зачем на задавать system, если вся информация о роли ассистента определяется буквально первым словом запроса? Не будет ли это миной замедленного действия, если вдруг потом кто-то переопределит роль на этапе инференса.
  • Почему вариантивность формата ответа на один и тот же вопрос — это плохо?
  • Низкопараметрические модели лучше обучать на молниеносный короткий ответ, чем на рассуждение?
  • Как влияет выбор target_modules на производительность? Мои эксперименты не позволяют дать статистически значимый ответ, есть ли зависимость или нет.
  • Что имеется в виду под round-trip: code → explanation → (ваш генератор) → code?
  • Имеет ли смысл использовать warmup_ratio для обучения на CPU?

Есть ещё некоторые вопросы, которые мне надо сперва самому проработать:
  • Как формалируются краевые случаи (edge cases) для jq?
  • Как писать примеры на плохотипизированные jq-выражения с объяснением особенностей работы этого jq-выражения? В вышеприведённой обучающей выборке пояснения слишком пространные. После типизации пояснения стали существенно лучше, но в предположении правильной типизации. А тут как быть?
  • Как подключить родной template? В прошлый раз были проблемы.
  • Как подключить packing? В прошлый раз были проблемы.
1
wound up as Aussie
462 / 114 / 20
Регистрация: 15.05.2019
Сообщений: 453
12.01.2026, 02:34
Цитата Сообщение от Mysterious Light Посмотреть сообщение
(на данный момент) открытые вопросы
Очень глубокие вопросы.
(в attachment, вначале продолжение, точнее развитие Вашей идеи выше, а где-то с середины ответы. )
Вложения
Тип файла: zip PipeLine_LoRAforQwen&QuestionsAnswering.zip (82.7 Кб, 7 просмотров)
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
12.01.2026, 02:34
Помогаю со студенческими работами здесь

Обучающая выборка, с чего начать
Мне нужна обучающая выборка для 52 рукописных символов, по 40 изображение *.bmp на каждый символ. ...

FANN, обучающая выборка
Здравствуйте, возник вопрос по обучающей выборке в библиотеке FANN. Пример из обучающей выборки...

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

Обучающая выборка из матриц разного размера
Как использовать подобную выборку для обучения? Возможно ли вообще её применить для обучения, или...

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


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 12.02.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами и вызывать обработчики событий столкновения. . . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 11.02.2026
Содержание блога Библиотека SDL3 содержит встроенные инструменты для базовой работы с изображениями - без использования библиотеки SDL3_image. Пошагово создадим проект для загрузки изображения. . .
SDL3 для Web (WebAssembly): Загрузка PNG с прозрачным фоном с помощью SDL3_image
8Observer8 10.02.2026
Содержание блога Библиотека SDL3_image содержит инструменты для расширенной работы с изображениями. Пошагово создадим проект для загрузки изображения формата PNG с альфа-каналом (с прозрачным. . .
Установка Qt-версии Lazarus IDE в Debian Trixie Xfce
volvo 10.02.2026
В общем, достали меня глюки IDE Лазаруса, собранной с использованием набора виджетов Gtk2 (конкретно: если набирать текст в редакторе и вызвать подсказку через Ctrl+Space, то после закрытия окошка. . .
SDL3 для Web (WebAssembly): Работа со звуком через SDL3_mixer
8Observer8 08.02.2026
Содержание блога Пошагово создадим проект для загрузки звукового файла и воспроизведения звука с помощью библиотеки SDL3_mixer. Звук будет воспроизводиться по клику мышки по холсту на Desktop и по. . .
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru