Энтузиаст запустил генеративный ИИ на 20-летнем PowerBook G4

Любитель винтажных вычислений решил запустить большую языковую модель (LLM), Llama2 на Apple PowerBook G4 2005 года. Компьютер оснащён процессором 1,5 ГГц, 1 Гб оперативной памяти и имеет ограниченное 32-битное адресное пространство. 

Энтузиаст разработал проект ullm. В основе проекта лежит Llama2.c от Андрея Карпати, минималистичная реализация LLM на чистом C.

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

Для работы на PowerPC:

добавили обёртки для системных функций;

устранили зависимости от exit;

реализовали поддержку big-endian (в отличие от little-endian в современных системах);

улучшили обработку ошибок и выделение памяти.

Затем он организовал код в библиотеку с открытым API, который отображается в заголовке. Это позволяет проводить модульное тестирование, чтобы гарантировать, что дальнейший рефакторинг не нарушит функциональность вывода.

// The runtime config for the inference operation. typedef struct { // The prompt to generate a response to. const char* prompt; // The path to the checkpoint file. const char* checkpoint_path; // The path to the tokenizer file. const char* tokenizer_path; // Model configuration. float temperature; float topp; unsigned int steps; // The source of entropy. uint64_t rng_seed; // The callback and context for generated output. void (*output_callback)(const char* token, void* cookie); void* cookie; } UllmLlama2RunConfig; // The runtime state for the inference engine. typedef struct { UllmFileHandle checkpoint_file; UllmLlama2Transformer transformer; UllmLlama2Tokenizer tokenizer; UllmLlama2Sampler sampler; } UllmLlama2State;

Полученный API чрезвычайно прост в тестировании и на его основе легко создавать инструменты интерфейса командной строки.

Энтузиаст работал с моделью TinyStories, которая не требует специализированного аппаратного ускорения. Он провёл тестирование с вариантом модели 15M, а затем переключился на самую точную из доступных 110M. 

aarossig@titanium:~/Projects/ullm$ make && time ./out/ullm.elf -c ../models/tinystories110M_be.bin -t ../models/tokenizer_be.bin -p «The quick brown fox jumped. Where did he go?» The quick brown fox jumped. Where did he go? He had been running around the forest all day. He was looking for something special. Suddenly, he saw a big tree. He stopped and looked up. He saw a big, brown bird in the tree. The bird was singing a beautiful song. The fox was so happy. He wanted to get closer to the bird. He jumped up and down, trying to reach the bird. But the bird was too high up. The fox was sad. He wanted to be friends with the bird. He looked around and saw a big, brown rock. He jumped on the rock and looked up at the bird. The bird saw the fox and flew down to him. The fox was so happy. He and the bird became friends. They played together in the forest every day. I ullm.llama2: Complete: 0.77 token/s real 4m0.099s user 3m51.387s sys 0m5.533s

На сервере с процессором Intel Xeon inference вывод шёл со скоростью 6.91 токенов в секунду. На PowerBook G4 — 0.77 токена/секунду. Таким образом, вывод работал примерно в девять раз медленнее, чем на ПК с современным процессором. Основной узкий момент — матричные операции, которые занимают большую часть времени при генерации текста.

void matmul(float* xout, const float* x, const float* w, int n, int d) { // W (d,n) @ x (n,) -> xout (d,) const float* wptr = w; const float* wptr_end = &wptr[n * d]; const float* xptr_end = &x[n]; while (wptr < wptr_end) { float sum = 0.0f; const float* xptr = x; while (xptr < xptr_end) { sum += *wptr++ * *xptr++; } *xout++ = sum; } }

Чтобы ускорить выполнение, автор переписал критическую функцию с использованием SIMD-инструкций AltiVec, доступных в процессорах PowerPC. Используя операцию fusion multiply-add, процессор может выполнять 4-кратное параллельное умножение и сложение с плавающей точкой, полагаясь на семантику SIMD (single instruction multiple data) для ускорения.

void matmul(float* xout, const float* x, const float* w, int n, int d) { // W (d,n) @ x (n,) -> xout (d,) const long addr_step = 4 * sizeof(float); const long w_addr_end = n * d * sizeof(float); const long x_addr_end = n * sizeof(float); long w_addr = 0; while (w_addr < w_addr_end) { __attribute__ ((aligned(16))) float psum[4] = {}; vector float sum_vec = vec_ld(0, &psum[0]); for (long x_addr = 0; x_addr < x_addr_end; x_addr += addr_step) { vector float w_vec = vec_ld(w_addr, w); vector float x_vec = vec_ld(x_addr, x); sum_vec = vec_madd(w_vec, x_vec, sum_vec); w_addr += addr_step; } vec_st(sum_vec, 0, psum); *xout++ = psum[0] + psum[1] + psum[2] + psum[3]; } }

AltiVec снизил время вывода примерно на 30 секунд, до 0.88 токена/секунду.

aarossig@titanium:~/Projects/ullm$ make && time ./out/ullm.elf -c ../models/tinystories110M_be.bin -t ../models/tokenizer_be.bin -p «The quick brown fox jumped. Where did he go?» The quick brown fox jumped. Where did he go? He had been running around the forest all day. He was looking for something special. Suddenly, he saw a big tree. He stopped and looked up. He saw a big, brown bird in the tree. The bird was singing a beautiful song. The fox was so happy. He wanted to get closer to the bird. He jumped up and down, trying to reach the bird. But the bird was too high up. The fox was sad. He wanted to be friends with the bird. He looked around and saw a big, brown rock. He jumped on the rock and looked up at the bird. The bird saw the fox and flew down to him. The fox was so happy. He and the bird became friends. They played together in the forest every day. I ullm.llama2: Complete: 0.88 token/s real 3m32.098s user 3m23.535s sys 0m5.368s

Автор отметил, что система G4 PowerPC ограничена максимальной адресуемой памятью в 4 ГБ, поэтому более крупные модели запустить не получится.

Подробнее об Apple Powerbook G4 можно прочитать здесь.

Источник: habr.com

0 0 голоса
Рейтинг новости
1
0
Подписаться
Уведомить о
0 комментариев
Межтекстовые Отзывы
Посмотреть все комментарии