diff --git a/Xenith/core.cpp b/Xenith/core.cpp index ed1837e..02b71fb 100644 --- a/Xenith/core.cpp +++ b/Xenith/core.cpp @@ -2,6 +2,19 @@ #include #include +#define USE_PARALLEL // Закомментируйте, чтобы отключить параллелизм +#define MAX_THREADS 0 // Укажите число ядер (0 — использовать все доступные) + +#ifdef USE_PARALLEL +#include +#define OMP_PARALLEL _Pragma("omp parallel for") +#define OMP_SET_THREADS() { if (MAX_THREADS > 0) omp_set_num_threads(MAX_THREADS); } +#else +#define OMP_PARALLEL +#define OMP_SET_THREADS() +#endif + + NeuralNetwork::NeuralNetwork(LayerStructure_t layers[], int count) : numLayers(count) { for (int i = 0; i < count; i++) sizes.push_back(layers[i].size); for (int i = 0; i < count - 1; i++) { @@ -19,45 +32,88 @@ NeuralNetwork::NeuralNetwork(LayerStructure_t layers[], int count) : numLayers(c } std::vector NeuralNetwork::feedForward(const std::vector& input) { + OMP_SET_THREADS(); // Применяем лимит ядер + outputs.clear(); outputs.push_back(input); + std::vector curr = input; + for (int i = 0; i < numLayers - 1; i++) { - std::vector next; - for (int j = 0; j < sizes[i+1]; j++) { - double sum = biases[i][j]; - for (int k = 0; k < (int)curr.size(); k++) sum += curr[k] * weights[i][j][k]; - next.push_back(1.0 / (1.0 + exp(-sum))); - } + // Заранее готовим вектор нужного размера для текущего слоя + std::vector next(sizes[i + 1]); + + // Распараллеливаем расчет каждого нейрона в слое + OMP_PARALLEL + for (int j = 0; j < sizes[i + 1]; j++) { + double sum = biases[i][j]; + + // Внутренний цикл обычно оставляем последовательным, + // так как создание потоков здесь даст больше тормозов, чем пользы + for (int k = 0; k < (int)curr.size(); k++) { + sum += curr[k] * weights[i][j][k]; + } + + // Сигмоида. Теперь пишем по индексу j — это безопасно для потоков + next[j] = 1.0 / (1.0 + exp(-sum)); + } + curr = next; outputs.push_back(curr); } return curr; } + double NeuralNetwork::train(const std::vector& input, const std::vector& target, double lr) { + // 0. Устанавливаем количество потоков (если включено в define) + OMP_SET_THREADS(); + + // 1. Прямой проход (получаем предсказание) std::vector pred = feedForward(input); + std::vector> errors(numLayers); - errors[numLayers-1].resize(sizes[numLayers-1]); + errors[numLayers - 1].resize(sizes[numLayers - 1]); + double totalErr = 0; - for (int i = 0; i < sizes[numLayers-1]; i++) { + + // 2. Вычисление ошибки на выходном слое + for (int i = 0; i < sizes[numLayers - 1]; i++) { double e = target[i] - pred[i]; - errors[numLayers-1][i] = e * pred[i] * (1.0 - pred[i]); + // Производная функции активации (сигмоиды): pred * (1 - pred) + errors[numLayers - 1][i] = e * pred[i] * (1.0 - pred[i]); totalErr += e * e; } + + // 3. Обратное распространение ошибки (Backpropagation) по скрытым слоям for (int i = numLayers - 2; i > 0; i--) { errors[i].resize(sizes[i]); - for (int j = 0; j < sizes[i]; j++) { - double e = 0; - for (int k = 0; k < sizes[i+1]; k++) e += errors[i+1][k] * weights[i][k][j]; - errors[i][j] = e * outputs[i][j] * (1.0 - outputs[i][j]); - } + + // Параллелим вычисления для каждого нейрона в слое + OMP_PARALLEL + for (int j = 0; j < sizes[i]; j++) { + double e = 0; + for (int k = 0; k < sizes[i + 1]; k++) { + e += errors[i + 1][k] * weights[i][k][j]; + } + // outputs[i][j] — это сохраненный результат активации этого нейрона из feedForward + errors[i][j] = e * outputs[i][j] * (1.0 - outputs[i][j]); + } } + + // 4. Обновление весов и смещений (Biases) for (int i = 0; i < numLayers - 1; i++) { - for (int j = 0; j < sizes[i+1]; j++) { - for (int k = 0; k < sizes[i]; k++) weights[i][j][k] += lr * errors[i+1][j] * outputs[i][k]; - biases[i][j] += lr * errors[i+1][j]; - } + + // Параллелим обновление весов следующего слоя + OMP_PARALLEL + for (int j = 0; j < sizes[i + 1]; j++) { + for (int k = 0; k < sizes[i]; k++) { + // Градиентный спуск: прибавляем (lr * ошибка * входной сигнал) + weights[i][j][k] += lr * errors[i + 1][j] * outputs[i][k]; + } + biases[i][j] += lr * errors[i + 1][j]; + } } + return totalErr; -} \ No newline at end of file +} diff --git a/Xenith/token/token.h b/Xenith/token/token.h index 095bb55..e525595 100644 --- a/Xenith/token/token.h +++ b/Xenith/token/token.h @@ -11,21 +11,22 @@ public: std::map idToWord; Tokenizer() { - add(""); // 0 - add("[SYS]"); // 1 - add("[USER]"); // 2 - add("[AI]"); // 3 - add(" "); // 4 - add("\n"); // 5 - add("привет"); // 6 - add("как"); // 7 - add("дела"); // 8 - add("?"); // 9 - add("СЏ"); // 10 - add("СЂРѕР±РѕС‚"); // 11 - add("хорошо"); // 12 + add(""); add("[SYS]"); add("[USER]"); add("[AI]"); add(" "); add("\n"); + add("."); add(","); add("!"); add("?"); add(":"); add(";"); add("-"); add("\""); add("("); add(")"); + add("Р°"); add("Р±"); add("РІ"); add("Рі"); add("Рґ"); add("Рµ"); add("С‘"); add("Р¶"); + add("Р·"); add("Рё"); add("Р№"); add("Рє"); add("Р»"); add("Рј"); add("РЅ"); add("Рѕ"); + add("Рї"); add("СЂ"); add("СЃ"); add("С‚"); add("Сѓ"); add("С„"); add("С…"); add("С†"); + add("С‡"); add("С€"); add("С‰"); add("СЉ"); add("С‹"); add("СЊ"); add("СЌ"); add("СЋ"); add("СЏ"); + add("Рё"); add("РІ"); add("РЅРµ"); add("РЅР°"); add("СЏ"); add("что"); add("тот"); add("быть"); + add("СЃ"); add("Р°"); add("весь"); add("это"); add("как"); add("РѕРЅР°"); add("РїРѕ"); add("РЅРѕ"); + add("РѕРЅРё"); add("Рє"); add("Сѓ"); add("ты"); add("РёР·"); add("РјС‹"); add("Р·Р°"); add("РІС‹"); + add("привет"); add("дела"); add("СЂРѕР±РѕС‚"); add("хорошо"); add("спасибо"); + add("РґР°"); add("нет"); add("РјРѕРіСѓ"); add("помочь"); add("знаю"); add("кто"); + add("РіРґРµ"); add("РєРѕРіРґР°"); add("почему"); add("хочу"); add("очень"); + add("тебя"); add("Р·РѕРІСѓС‚"); add("BiPy"); } + void add(std::string word); int getID(std::string word); std::string getWord(int id); diff --git a/Xenith/typedef.h b/Xenith/typedef.h index 348e38c..fcb6083 100644 --- a/Xenith/typedef.h +++ b/Xenith/typedef.h @@ -1,9 +1,9 @@ #ifndef TYPEDEF_H #define TYPEDEF_H -const int MAX_CONTEXT = 4; // Сколько токенов РІРёРґРёС‚ сеть +const int MAX_CONTEXT = 8; // Сколько токенов РІРёРґРёС‚ сеть const int EMBED_DIM = 4; // Размер вектора РѕРґРЅРѕРіРѕ токена -const int MAX_VOCAB = 13; // Размер словаря +const int MAX_VOCAB = 90; // Размер словаря typedef enum { SIGMOID } FunctionActivate_t; typedef struct { int size; FunctionActivate_t activate; } LayerStructure_t; diff --git a/dataset.txt b/dataset.txt index 38b9c75..b9d0172 100644 --- a/dataset.txt +++ b/dataset.txt @@ -1 +1 @@ -[USER]привет[AI]привет как дела? \ No newline at end of file +[SYS]ты полезный СЂРѕР±РѕС‚ помощник[USER]привет[AI]привет как дела? \ No newline at end of file diff --git a/main b/main deleted file mode 100755 index a7ad854..0000000 Binary files a/main and /dev/null differ diff --git a/main.cpp b/main.cpp index 04e8e20..d6bf2c5 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -7,32 +8,16 @@ #include #include "Xenith/core.h" #include "Xenith/token/token.h" +#include +#include -// Глобальные настройки -std::string currentSystemPrompt = "СЏ СЂРѕР±РѕС‚"; +std::string currentSystemPrompt = ""; -void printParameterCount(LayerStructure_t layers[], int numLayers) { - long long totalParams = 0; - for (int i = 0; i < numLayers - 1; i++) { - long long weights = (long long)layers[i].size * layers[i + 1].size; - long long biases = (long long)layers[i + 1].size; - totalParams += (weights + biases); - } - - std::cout << "--- Xenith AI (Model Size: "; - if (totalParams >= 1000000000000LL) - std::cout << std::fixed << std::setprecision(1) << (double)totalParams / 1000000000000.0 << "t"; - else if (totalParams >= 1000000000LL) - std::cout << std::fixed << std::setprecision(1) << (double)totalParams / 1000000000.0 << "b"; - else if (totalParams >= 1000000LL) - std::cout << std::fixed << std::setprecision(1) << (double)totalParams / 1000000.0 << "m"; - else if (totalParams >= 1000LL) - std::cout << std::fixed << std::setprecision(1) << (double)totalParams / 1000.0 << "k"; - else - std::cout << totalParams; - - std::cout << " parameters) ---" << std::endl; -} +LayerStructure_t layers[] = { + {MAX_CONTEXT * EMBED_DIM, SIGMOID}, + {16, SIGMOID}, + {MAX_VOCAB, SIGMOID} +}; std::vector buildNetInput(const std::vector& tokens, Embedder& emb) { std::vector netInput; @@ -58,42 +43,89 @@ void trainOnSequence(NeuralNetwork& nn, Tokenizer& tok, Embedder& emb, const std std::cout << "Error: Sequence too short for training." << std::endl; return; } + int numLayers = sizeof(layers) / sizeof(layers[0]); + long long totalParams = 0; + for (int i = 0; i < numLayers - 1; i++) { + totalParams += (long long)layers[i].size * layers[i + 1].size + layers[i + 1].size; + } + + std::string modelSizeStr; + { + std::stringstream ss; + if (totalParams >= 1e12) ss << std::fixed << std::setprecision(1) << totalParams / 1e12 << "t"; + else if (totalParams >= 1e9) ss << std::fixed << std::setprecision(1) << totalParams / 1e9 << "b"; + else if (totalParams >= 1e6) ss << std::fixed << std::setprecision(1) << totalParams / 1e6 << "m"; + else if (totalParams >= 1e3) ss << std::fixed << std::setprecision(1) << totalParams / 1e3 << "k"; + else ss << totalParams; + modelSizeStr = ss.str(); + } + + std::string sequenceStr = ""; + for (int tId : allTokens) { + sequenceStr += "{" + tok.getWord(tId) + " (" + std::to_string(tId) + ")} "; + } + + auto startTime = std::chrono::high_resolution_clock::now(); + int trainSteps = 0; + double stepsPerSec = 0, maxLoss = 0; std::cout << "Training logic: Next Token Prediction..." << std::endl; + + std::cout << "\033[s\033[999;1H" << "\033[2K" << "\033[1;30m" << "\033[F" << "\r" + << "DATA: " << (sequenceStr.length() > 100 ? sequenceStr.substr(0, 200) : sequenceStr) << "\033[0m\033[u"; + for (int e = 1; e <= epochs; e++) { double totalLoss = 0; for (size_t i = 1; i < allTokens.size(); i++) { - std::vector context; - for (size_t j = 0; j < i; j++) context.push_back(allTokens[j]); - + std::vector context(allTokens.begin(), allTokens.begin() + i); std::vector target(MAX_VOCAB, 0.0); target[allTokens[i]] = 1.0; - totalLoss += nn.train(buildNetInput(context, emb), target, lr); + + trainSteps++; + auto currentTime = std::chrono::high_resolution_clock::now(); + if (std::chrono::duration(currentTime - startTime).count() >= 1.0) { + stepsPerSec = trainSteps / std::chrono::duration(currentTime - startTime).count(); + trainSteps = 0; + startTime = currentTime; + } + std::cout << "\rEpoch " << std::setw(4) << e << "/" << epochs + << " | Token: " << std::setw(3) << i << "/" << allTokens.size() + << " | Loss: " << std::fixed << std::setprecision(6) << totalLoss + << " | Max Loss: " << std::fixed << std::setprecision(6) << maxLoss << " \033[s"; + + std::cout << "\033[999;1H" << "\r"; + + std::cout << "SPEED: " << std::setw(6) << std::fixed << std::setprecision(1) << stepsPerSec << " st/s" + << " | MODEL: " << std::setw(7) << modelSizeStr + << " | CURRENT: [" << std::left << std::setw(15) << tok.getWord(allTokens[i]) << "] (" + << std::right << std::setw(4) << allTokens[i] << ") "; + + std::cout << "\033[K" << "\033[0m"; + + std::cout << "\033[997;1H" << "\r" << std::flush << "\033[u"; + } - std::cout << "\rEpoch " << e << "/" << epochs << " | Loss: " << std::fixed << std::setprecision(6) << totalLoss << std::flush; + maxLoss = totalLoss; } std::cout << "\nDone!" << std::endl; } + + int main() { + SetConsoleOutputCP(CP_UTF8); + SetConsoleCP(CP_UTF8); + Tokenizer tok; Embedder emb(MAX_VOCAB, EMBED_DIM); - LayerStructure_t layers[] = { - {MAX_CONTEXT * EMBED_DIM, SIGMOID}, - {16, SIGMOID}, - {MAX_VOCAB, SIGMOID} - }; + int numLayers = sizeof(layers) / sizeof(layers[0]); NeuralNetwork nn(layers, numLayers); - printParameterCount(layers, numLayers); - - std::cout << "\n--- MENU ---" << std::endl; - std::cout << "/train\n/trainFile\n/help\n/exit\n"; while (true) { - std::cout << "\nxentith~$ "; + std::cout << "xentith~$ "; std::string cmdIn; std::getline(std::cin, cmdIn); @@ -164,30 +196,74 @@ int main() { std::getline(std::cin, currentSystemPrompt); std::cout << "System Prompt updated!" << std::endl; - } else if (cmdIn == "/help") { + } + else if (cmdIn == "/help") { std::cout << "\n--- MENU ---" << std::endl; std::cout << "/train\n/trainFile\n/sysPrompt\n/help\n/exit\n"; + } else if (cmdIn == "/clr") { + + std::cout << "\033[2J\033[1;1H"; + } else { std::string prompt = "[SYS]" + currentSystemPrompt + "[USER]" + cmdIn + "[AI]"; std::vector currentTokens = tok.textToTokens(prompt); std::cout << "AI: "; - for (int g = 0; g < 30; g++) { + long long totalParams = 0; + for (int i = 0; i < numLayers - 1; i++) { + long long weights = (long long)layers[i].size * layers[i + 1].size; + long long biases = (long long)layers[i + 1].size; + totalParams += (weights + biases); + } + std::string modelSizeStr; + { + std::stringstream ss; + if (totalParams >= 1000000000000LL) ss << std::fixed << std::setprecision(1) << (double)totalParams / 1000000000000.0 << "t"; + else if (totalParams >= 1000000000LL) ss << std::fixed << std::setprecision(1) << (double)totalParams / 1000000000.0 << "b"; + else if (totalParams >= 1000000LL) ss << std::fixed << std::setprecision(1) << (double)totalParams / 1000000.0 << "m"; + else if (totalParams >= 1000LL) ss << std::fixed << std::setprecision(1) << (double)totalParams / 1000.0 << "k"; + else ss << totalParams; + modelSizeStr = ss.str(); + } + + // Переменные для замера скорости + auto startTime = std::chrono::high_resolution_clock::now(); + int tokensInSecond = 0; + double tokensPerSec = 0; + + for (int g = 0; g < 1024; g++) { std::vector out = nn.feedForward(buildNetInput(currentTokens, emb)); - + int bestId = 0; for (int i = 0; i < MAX_VOCAB; i++) { if (out[i] > out[bestId]) bestId = i; } if (bestId == 0) break; - + + tokensInSecond++; + auto currentTime = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed = currentTime - startTime; + + if (elapsed.count() >= 0.1) { + tokensPerSec = tokensInSecond / elapsed.count(); + tokensInSecond = 0; + startTime = currentTime; + } + std::string word = tok.getWord(bestId); std::cout << word << std::flush; - + + std::cout << "\033[s" << "\033[999;1H" << "\033[2K" + << "--- [ID: " << bestId << "] | " + << "Speed: " << std::fixed << std::setprecision(1) << tokensPerSec*10 << " t/s | " + << "Model: " << modelSizeStr << " params ---" + << "\033[u" << std::flush; + currentTokens.push_back(bestId); } + // Чтобы РєСѓСЂСЃРѕСЂ РЅРµ остался РІРЅРёР·Сѓ после генерации std::cout << std::endl; } }