ساخت یک فشرده‌ساز پایتون با استفاده از توکن‌ساز OpenAI؛ چه نتایجی به دست آمد؟

hackernoon.com

انتشار در منبع: انتشار در سایت ما:

مقاله اصلی · English

مدل‌های زبانی بزرگ میلیاردها دلار برای یادگیری یکی از بهترین واژگان زیرواژه‌ای موجود هزینه کرده‌اند؛ واژگانی شامل حدود ۲۰۰ هزار واحد زبانی که بر اساس میزان کمکشان به فشرده‌سازی رتبه‌بندی شده‌اند. هر بار که به این واژگان نگاه می‌کردم، یک سؤال به ذهنم می‌رسید: چرا نباید بتوان از این واژگان مستقیماً برای فشرده‌سازی داده‌ها استفاده کرد؟

TokPress یک فشرده‌ساز بدون اتلاف است که تماماً با پایتون پیاده‌سازی شده و برای تبدیل ورودی به توکن از واژگان o200k_base متعلق به OpenAI استفاده می‌کند. سپس الگوریتم LZ77 را در سطح توکن اجرا می‌کند و نتیجه را با روش کدگذاری آنتروپی rANS فشرده می‌سازد. برای مجموعه‌هایی از رکوردهای کوچک، هم‌ساختار و مشابه از نظر شِما، مانند گزارش‌ها، داده‌های JSON و تله‌متری، استفاده از فرهنگ واژگان آموزش‌دیده نسبت فشرده‌سازی را به ۰٫۲۵۶۵ می‌رساند. در حالت دسته‌ای، این نسبت به ۰٫۰۸۷۵ می‌رسد؛ در حالی که فشرده‌سازی جداگانه هر رکورد ممکن است حجم آن را افزایش دهد و نسبت فشرده‌سازی را از ۱ بیشتر کند.

بنابراین، تصمیم گرفتم چنین ابزاری بسازم. حاصل کار، پروژه‌ای بود که در یک آخر هفته، تنها با پایتون و بدون کد بومی و با مصرف فنجان‌های قهوه بیش از حد لازم نوشتم. نامش را TokPress گذاشتم. این ابزار ورودی را با استفاده از o200k_base، همان توکن‌سازی که مدل‌های OpenAI به کار می‌برند، به توکن تبدیل می‌کند؛ سپس LZ77 را در سطح توکن اجرا می‌کند و در پایان، با روش rANS نتیجه را از طریق کدگذاری آنتروپی فشرده می‌سازد. در جریان توسعه، به عددی رسیدم که هنوز هم لبخند به لبم می‌آورد: ۱۵۰ خط گزارش JSON که در قالب یک جریان واحد فشرده شدند و حجمشان به ۰٫۰۸۷۵ برابر اندازه اولیه رسید؛ اما وقتی هر خط را جداگانه فشرده کردم، حجم داده‌ها بیشتر شد.

این روایت واقعی ماجراست؛ از جمله بخش‌هایی که خراب شدند و مشکلاتی که باید برطرف می‌کردم.

چرا فشرده‌سازی تعداد زیادی رکورد کوچک واقعاً دشوار است؟

فشرده‌سازهای متداولی مانند gzip و zstd عمدتاً برای فایل‌های بزرگ طراحی شده‌اند. آن‌ها مدل خود را از جریان داده یاد می‌گیرند؛ رویکردی که وقتی چندین مگابایت داده برای جبران هزینه‌های اولیه در اختیارشان باشد، عملکرد خوبی دارد. اما بخش بزرگی از داده‌های دنیای واقعی از رکوردهای کوچک، مستقل و دارای ساختار مشابه تشکیل شده است: خطوط گزارش JSON، رویدادهای تله‌متری، پاسخ‌های API و فراداده بسته‌های نرم‌افزاری. حجم هر رکورد ممکن است فقط ۲۰۰ تا ۵۰۰ بایت باشد و هر رکورد نیز به‌صورت مستقل ذخیره یا پردازش شود. من به‌عنوان مهندسی که بیشتر روزهای کاری‌ام را صرف ساخت سامانه‌های یادگیری ماشین در محیط عملیاتی می‌کنم و آخر هفته‌ها نیز به ارزیابی و مقایسه توکن‌سازها می‌پردازم، مرتب با چنین داده‌هایی سروکار دارم.

اگر یک خط گزارش ۲۰۰ بایتی را با gzip فشرده کنید، ممکن است سربار سرآیند و راه‌اندازی فرهنگ واژگان از مقدار داده‌ای که ذخیره کرده‌اید بیشتر باشد. در اندازه‌گیری‌های من، یک نمونه JSON با حجم تنها ۷۰ بایت پس از فشرده‌سازی به ۸۱ بایت رسید؛ یعنی فشرده‌ساز به‌جای کوچک‌تر کردن داده، آن را بزرگ‌تر کرد. این رفتار باگ نیست، بلکه هزینه شروع کار بدون داشتن مدل ازپیش‌آماده است.

راهکار zstandard برای این مشکل، حالت استفاده از فرهنگ واژگان است: یک فرهنگ مشترک به‌صورت آفلاین آموزش داده می‌شود و سپس هر رکورد با استفاده از آن فشرده می‌شود. MongoDB و Amazon DocumentDB نیز اکنون از همین رویکرد استفاده می‌کنند که می‌تواند نسبت فشرده‌سازی اسناد JSON را تا پنج برابر بهبود دهد. در سال ۲۰۲۵، گروه ویژه مهندسی اینترنت (IETF) استاندارد RFC 9842 را منتشر کرد که فشرده‌سازی مبتنی بر فرهنگ واژگان را مستقیماً وارد پروتکل HTTP می‌کند؛ قابلیتی برای استفاده مرورگرها، سرورها و دیگر اجزای وب. در این استاندارد، پاسخ‌های API با ساختار شناخته‌شده نیز صراحتاً از موارد مناسب برای این روش معرفی شده‌اند.

پس با مسئله‌ای واقعی و دارای هزینه اقتصادی روبه‌رو هستیم: تعداد زیادی رکورد کوچک و هم‌ساختار که می‌توانند از یک فرهنگ واژگان مشترک بهره ببرند. من فقط می‌خواستم یک ایده تازه را امتحان کنم: استفاده از یک توکن‌ساز به‌عنوان فرهنگ واژگان.

flowchart LR
 A["رکورد بایتی x"] --> B["۱. توکن‌ساز BPE با حفظ دقیق بایت‌ها: o200k_base یا واژگان آموزش‌دیده"]
 B -->|"شناسه‌های توکن"| C["۲. اجرای LZ77 در سطح توکن: تجزیه‌گر حریص مبتنی بر هش، طول تطبیق l >= 3، پرچم = اندازه واژگان"]
 C -->|"توکن‌های مستقیم و تاپل‌های تطبیق"| D["۳. آنتروپی (rANS) + جریان بیت: خام، تنک، تفکیک‌شده، تطبیقی، تطبیقی-تفکیک‌شده، PPM، آبشار فرهنگ واژگان"]
 D -->|"جریان‌های نامزد"| E["حداقل‌سازی: ساخت همه نامزدها و انتخاب کوچک‌ترین"]
 E --> F["جریان .tokz / .tokbi"]
 G["نمونه آموزش: N رکورد هم‌ساختار"] --> H["آموزش آفلاین: train-vocab ← .ranks، train-dict ← .tokdict"]
 H -->|"واژگان"| B
 H -->|"بافر اولیه برای سابقه تطبیق"| C
 H -->|"جدول‌های ثابت مرتبه صفر و یک"| D
 G -.->|"compress_many: N رکورد در یک جریان تطبیقی"| F

مرحله اول ـ ایده اصلی: توکن‌ساز همان فرهنگ واژگان آماده است

ایده ساده است. وظیفه توکن‌ساز مبتنی بر BPE این است که واحدهای زبانی را پیدا کند که نمایش فشرده‌تری از متن ارائه می‌دهند. برای مثال، "action" یک توکن است و "click" نیز یک توکن مستقل محسوب می‌شود. توکن‌ساز از قبل، در مقیاسی بسیار بزرگ، کار دشوار ساخت واژگان را انجام داده است و می‌توان از نتیجه آن بدون نیاز به آموزش مجدد یک واژگان عمومی استفاده کرد.

بنابراین، ورودی را به توکن تبدیل می‌کنیم، الگوریتم LZ77 را به‌جای بایت‌ها روی شناسه‌های توکن اجرا می‌کنیم و سپس با rANS کدگذاری آنتروپی را انجام می‌دهیم. جریان توکن‌ها الفبایی مناسب‌تر از بایت‌های خام در اختیار مدل قرار می‌دهد، زیرا ساختار واحدهای چندبایتی از همان ابتدا در توکن‌ها منعکس شده است.

import tokpress

compressed = tokpress.compress(payload) # بایت یا رشته
original = tokpress.decompress(compressed) # بازسازی دقیق بایت‌ها

یک شرط اساسی وجود دارد که نمی‌توان از آن چشم‌پوشی کرد: بازسازی باید از نظر بایتی کاملاً دقیق باشد، حتی اگر ورودی داده‌ای باینری و دلخواه باشد. گزارش‌ها همیشه UTF-8 معتبر نیستند. تابع عمومی encode در کتابخانه tiktoken رشته str می‌گیرد، اما جفت توابع داخلی _encode_bytes و decode_bytes مستقیماً با بایت‌های خام کار می‌کند؛ حتی با بایتی مانند 0xFF که به‌تنهایی UTF-8 معتبر نیست. من از همین روش استفاده می‌کنم تا هر ورودی بدون تغییر بایت‌ها بازیابی شود.

مرحله دوم ـ نخستین مسیر اشتباه، که درس خوبی هم داشت

اولین ایده‌ام ساخت واژگانی اختصاصی برای یک حوزه مشخص بود. می‌خواستم با آموزش روی گزارش‌های JSON، قطعات پرتکرار را استخراج کنم و توکن‌سازی کوچک‌تری بسازم که دقیقاً برای داده‌های خودم مناسب باشد. ایده جذابی به نظر می‌رسید، اما در عمل بی‌سروصدا شکست خورد.

مشکل این بود که یک واژگان محدود تنها زمانی توکن‌ساز معتبری محسوب می‌شود که زنجیره ادغام سلسله‌مراتبی BPE کاملی داشته باشد؛ یعنی هر توکن باید از به‌هم‌پیوستن دو توکن با رتبه پایین‌تر ساخته شده باشد. استخراج قطعات بر اساس طولانی‌ترین تطبیق، صرفاً مجموعه‌ای از رشته‌ها تولید می‌کند، نه زنجیره‌ای از ادغام‌های معتبر. در نتیجه، رمزگذار نمی‌تواند همان ادغام‌ها را بازتولید کند و توکن‌ها آن‌طور که انتظار داریم به شکل اولیه بازنمی‌گردند. در نهایت، کل سامانه ساخت پروفایل‌های اختصاصی حوزه را کنار گذاشتم و این درس را به‌سختی یاد گرفتم:

واژگانی که نمی‌توان ادغام‌های آن را بازسازی کرد، قابل اعتماد نیست.

مرحله سوم ـ سه باگ واقعی که فقط با بدبینی مهندسی پیدا شدند

بخش اصلی کار آن آخر هفته، ساخت کدگذار آنتروپی بود. پیاده‌سازی rANS با دقت جدول ۲ به توان ۱۶ و حالت ۶۴ بیتی انجام شد. همچنین انتخاب‌گری ساختم که برای هر رکورد حداکثر هشت روش کدگذاری نامزد را تولید می‌کند و کوچک‌ترین خروجی را نگه می‌دارد. این طراحی مزیت مهمی دارد: دستور min(candidates, key=len) باعث می‌شود هیچ روش خاصی به‌اجبار انتخاب نشود و اندازه خروجی تعیین‌کننده باشد.

اما در سه مورد جالب، اندازه خروجی به نتیجه‌ای اشتباه منجر می‌شد. هر سه باگ را فقط به این دلیل پیدا کردم که ورودی‌های تصادفی و متنوع را بارها آزمایش کردم، نه اینکه به یک نمونه ثابت بسنده کنم.

  1. قطع شدن مقدار تک‌نمادی. اگر جدول دقیقاً یک نماد فعال داشته باشد، فراوانی آن برابر با ۶۵۵۳۶، یعنی احتمال ۱۰۰ درصد، خواهد بود. نمایش این مقدار به ۱۷ بیت نیاز دارد. من آن را در ۱۶ بیت ذخیره می‌کردم و مقدار ۶۵۵۳۶ هنگام انتقال، بی‌سروصدا به صفر تبدیل می‌شد. راه‌حل، انتقال مقدار freq - 1 بود.
  2. ترتیب اشتباه در مسیر گریز. الگوریتم rANS داده‌ها را برخلاف ترتیب منطقی آن‌ها کدگذاری می‌کند. در یک زنجیره دومرحله‌ای که ابتدا از جدول وابسته به بافت استفاده می‌کند و سپس به مدل مرتبه صفر بازمی‌گردد، ترتیب فراخوانی‌های رمزگذاری باید برعکس ترتیب جزئی مصرف رویدادها توسط رمزگشا باشد. اگر این ترتیب اشتباه باشد، خروجی فقط در ورودی‌هایی خراب می‌شود که از مسیر گریز استفاده می‌کنند؛ بنابراین، مشکل ممکن است تا زمان بروز یک مورد خاص پنهان بماند.
  3. معکوس‌سازی دوباره. در حالت adaptive-split، فهرست نمادهای مسیر گریز طی یک پیمایش رو به جلو ساخته می‌شود؛ برخلاف تمام حالت‌های دیگر. من با تقلید از سایر حالت‌ها، آن را برای بار دوم معکوس کردم. این کار اشتباه بود. مقادیر فقط زمانی به‌هم‌ریخته می‌شدند که یک رکورد بیش از یک نماد گریز داشت.

سه باگ، سه آزمون رگرسیون و بدون دردسر اضافه. این همان بخش فشرده‌سازی است که معمولاً در تیترهای وبلاگی جایی ندارد، اما حدود ۹۰ درصد کار واقعی را تشکیل می‌دهد: قالب باینری باید در رمزگذاری و رمزگشایی کاملاً متقارن باشد و تنها راه اطمینان از صحت آن، آزمایش گسترده و مکرر با ورودی‌های متنوع است.

مرحله چهارم ـ فرهنگ واژگان و عددی که ایده را روشن کرد

بهبود اصلی با ساخت TokDict حاصل شد. این فرهنگ واژگان یک بار با استفاده از نمونه‌ای از رکوردهای هم‌ساختار آموزش داده می‌شود و سپس هر رکورد جدید از سه مزیت آماده بهره می‌برد: نخست، یک بافر اولیه برای LZ که امکان تطبیق با سابقه رکوردهای دیگر را فراهم می‌کند؛ دوم، یک جدول rANS ثابت مرتبه صفر که دیگر لازم نیست برای هر رکورد در جریان خروجی ذخیره شود؛ و سوم، جدول‌های بافت مرتبه یک برای رایج‌ترین بافت‌های توکن قبلی، همراه با زنجیره‌ای از مسیرهای گریز که اجازه می‌دهد رکورد حاوی داده‌هایی باشد که هرگز در فرهنگ واژگان دیده نشده‌اند.

tokpress train-dict mydict.tokdict sample1.json sample2.json ...
tokpress compress new_record.json --dict mydict.tokdict -o new_record.tokz
tokpress decompress new_record.tokz --dict mydict.tokdict -o restored.json

نتایج روی ۲۳۰ رکورد گزارش ساختاریافته که ۱۸۴ رکورد آن برای آموزش و ۴۶ رکورد برای آزمون استفاده شده بود و در مرحله آموزش دیده نشده بودند، به این صورت بود:

مرحلهنسبت فشرده‌سازی
فشرده‌سازی هر رکورد به‌صورت مستقل، بدون فرهنگ واژگان۰٫۸۰۰
افزودن جدول مرتبه صفر TokDict۰٫۲۸۴
افزودن جدول‌های بافت مرتبه یک۰٫۲۵۶۵
gzip با سطح فشرده‌سازی ۹۰٫۷۲۸
zstd با سطح ۱۹، بدون فرهنگ واژگان۰٫۷۳۷
zstd با سطح ۱۹ و فرهنگ واژگان آموزش‌دیده و متناسب با داده‌ها۰٫۱۹۵

نسبت به gzip و zstd بدون فرهنگ واژگان، نتیجه TokPress بهتر بود. در مقایسه با zstd که از همان داده‌های آموزشی استفاده می‌کرد، فاصله عملکرد به حدود ۱٫۳ برابر رسید؛ در حالی که بدون فرهنگ واژگان، اختلاف حدود پنج تا هفت برابر بود. البته باید صادقانه گفت که روش‌های آموزش فرهنگ واژگان COVER و FastCover در zstd از روش من که بر الحاق داده‌ها برای ایجاد سابقه اولیه تکیه دارد، پخته‌تر هستند و zstd همچنان روی همین داده‌ها برنده است. ادعایی خلاف این ندارم.

سپس به حالت دسته‌ای رسیدم؛ قابلیتی که در واقع مهم‌ترین دلیل انتشار پروژه است. به‌جای فشرده‌سازی مستقل هر رکورد، همه رکوردها به هم متصل می‌شوند و به‌صورت یک جریان واحد فشرده می‌شوند. طول هر رکورد در سرآیند ذخیره می‌شود تا هنگام رمزگشایی بتوان هر رکورد را دقیقاً و به‌صورت مستقل بازیابی کرد.

packed = tokpress.compress_many(records)
records = tokpress.decompress_many(packed) # بازسازی دقیق بایت‌ها، رکورد به رکورد

در آزمایشی روی ۱۵۰ رکورد JSON هم‌ساختار با حجم کلی حدود ۱۱٫۶ کیلوبایت، مجموع نسبت فشرده‌سازی در حالت پردازش جداگانه به ۱٫۱۹ رسید؛ یعنی حجم داده‌ها عملاً افزایش یافت. اما با فشرده‌سازی همه آن‌ها در قالب یک جریان تطبیقی، این نسبت به ۰٫۰۸۷۵ رسید. داده‌های ورودی یکسان بودند، اما به‌جای ۱۵۰ سرآیند فقط یک سرآیند داشتیم و یک مدل تطبیقی می‌توانست از اطلاعات کل مجموعه استفاده کند، نه اینکه برای هر رکورد از نقطه صفر شروع کند. این عدد، تمام ایده اصلی پروژه را در یک مثال خلاصه می‌کند.

مرحله پنجم ـ نکته مهم: جادوی اصلی در توکن‌سازی نیست

بخش ناخوشایند ماجرا یک نتیجه نظری است: توکن‌سازی نمی‌تواند میزان اطلاعات موجود در یک پیام را کاهش دهد. چون این تبدیل یک‌به‌یک است، آنتروپی را حفظ می‌کند. بنابراین، هر بهبود واقعی در فشرده‌سازی باید از مدل‌سازی ناشی شود، نه از خود تبدیل.

این موضوع را در عمل هم بررسی کردم. در حالت دسته‌ای و بدون فرهنگ واژگان آموزش‌دیده، TokPress صرفاً زنجیره‌ای از LZ و کدگذاری آنتروپی است. با این حال، در متن‌های طولانی از gzip بهتر عمل می‌کند و در مجموعه آزمایشی متن‌های کوتاه حتی از zstd نیز جلو می‌زند: نسبت فشرده‌سازی TokPress برابر ۰٫۲۹۸ بود، در مقایسه با ۰٫۳۲۴ برای zstd و ۰٫۳۰۶ برای brotli. اما این بهبود حاصل کدگذاری آنتروپی است، نه خود توکن‌ساز. اگر توکن‌ساز را حذف کنیم، میزان برتری کاهش می‌یابد. نقش توکن‌ساز این است که الفبای داده را به شکلی بازآرایی کند تا یک مدل مرتبه پایین بتواند ساختارهایی را تشخیص دهد که شناسایی آن‌ها با مدل بایتی به مرتبه بالاتر نیاز دارد. توکن‌سازی ابزاری برای بهبود مدل است، نه راهی برای دستیابی رایگان به فشرده‌سازی بهتر.

این نتیجه همچنین جایگاه TokPress را در مقایسه با نزدیک‌ترین پروژه مشابه آن روشن می‌کند. پروژه parmar در بررسی ۴۵۲ پیکربندی مختلف برای روش «ابتدا توکن‌سازی، سپس فشرده‌سازی» نشان داده بود که اجرای tiktoken پیش از xz، در مقایسه با xz معمولی، نسبت فشرده‌سازی را ۷ تا ۹٫۶ درصد بهبود می‌دهد. اما در آن روش، شناسه‌های توکن به یک فشرده‌ساز بایتی سپرده می‌شوند و کد، JSON و گزارش‌ها نیز صراحتاً از مجموعه داده‌های آزمایش کنار گذاشته شده بودند. TokPress مسیر دیگری را انتخاب می‌کند: شناسه‌های توکن را مستقیماً با rANS کدگذاری آنتروپی می‌کند و دقیقاً برای مجموعه‌هایی از رکوردهای کوچک و هم‌ساختار طراحی شده است که parmar بررسی نکرده بود. هر دو پروژه از فرض اولیه مشابهی شروع می‌کنند، اما دو بخش متفاوت از فضای طراحی را بررسی می‌کنند. نتیجه آزمایش‌های من این است که برای متن‌هایی که در قالب فایل کامل فشرده می‌شوند، کدگذاری مستقیم توکن‌ها از سپردن آن‌ها به xz بهتر عمل می‌کند؛ با این حال، هیچ‌یک جایگزین فرهنگ واژگان آموزش‌دیده نیست.

نتیجه مهم دیگر این بود که فرهنگ واژگان آموزش‌دیده به شِمای داده و حوزه کاربرد وابسته است. اگر آن را با گزارش‌های JSON آموزش دهید و سپس برای کد پایتون به کار ببرید، عملاً هیچ مزیتی نسبت به استفاده نکردن از فرهنگ واژگان به دست نمی‌آورید: نسبت فشرده‌سازی در حالت دارای فرهنگ واژگان ۰٫۴۸۹ و در حالت بدون آن ۰٫۴۸۵ بود. این فرهنگ واژگان واقعاً یک ابزار تخصصی برای حوزه‌ای مشخص است و تنها زمانی باید از آن استفاده کرد که داده‌های هدف به داده‌های آموزشی شباهت داشته باشند.

در جریان کار، به نکته‌ای کمتر آشکار درباره پیاده‌سازی tiktoken هم پی بردم. تابع _encode_bytes برای داده‌های UTF-8 معتبر از عبارت منظم pat_str استفاده می‌کند و BPE را برای هر قطعه به‌صورت جداگانه اجرا می‌کند. بنابراین، اگر واژگان با اجرای ساده BPE روی کل ورودی آموزش داده شوند، ممکن است قطعات حاصل با مرزهای توکن‌سازی تیکتاکن سازگار نباشند. راه‌حل این بود که فرایند آموزش را مطابق رفتار واقعی tiktoken انجام دهم: ابتدا داده را با همان عبارت منظم به قطعات اولیه تقسیم کنم و سپس ادغام میان قطعات مختلف را ممنوع کنم. پس از این تغییر، واژگان آموزش‌دیده با داده‌های JSON در آزمون روی JSONهای دیده‌نشده، به نسبت فشرده‌سازی ۰٫۱۶۲ رسید؛ در حالی که این مقدار برای o200k_base برابر با ۰٫۱۷۸ بود. اکنون پروژه دو فرمان tokpress train-vocab و tokpress fit را نیز ارائه می‌کند؛ فرمان دوم هم‌زمان واژگان و فرهنگ واژگان فشرده‌سازی را آموزش می‌دهد.

جعبه‌ابزار، یک هفته بعد

این پروژه آخر هفته‌ای، پیش از آنکه متوجه شوم، به یک رابط خط فرمان کامل تبدیل شده بود:

tokpress compress record.json -o record.tokz
tokpress decompress record.tokz -o restored.json
tokpress pack batch.tokz record1.json record2.json ... # یک جریان تطبیقی
tokpress unpack batch.tokz out_dir/
tokpress read batch.tokz 7 # دسترسی تصادفی با پیچیدگی O(1)
tokpress train-dict mydict.tokdict samples.jsonl
tokpress train-vocab myvocab.ranks corpus.txt
tokpress fit out corpus.txt # آموزش هم‌زمان هر دو
tokpress tokenize-stats file.txt # توکن در هر کیلوبایت، آنتروپی و اطلاعات متقابل

فرمان آخر را می‌توان نوعی ادای احترام کوچک به جامعه مدل‌های زبانی بزرگ دانست. این فرمان آنتروپی توکن‌ها در مرتبه صفر و یک و همچنین اطلاعات متقابل توکن‌های مجاور را برای هر مجموعه متنی محاسبه می‌کند. به این ترتیب، کیفیت توکن‌ساز از طریق میزان فشرده‌سازی سنجیده می‌شود؛ رویکردی که گلدمن و همکاران نشان داده‌اند با عملکرد واقعی مدل همبستگی دارد. کافی است مجموعه ارزیابی خود را توکن‌سازی کنید تا عددی به دست آورید که نشان دهد توکن‌ساز تا چه اندازه با آن مجموعه سازگار است.

کل پروژه عمداً بهینه‌سازی گسترده نشده است: حدود ۲۹۰۰ خط کد خالص پایتون و ۹۳ آزمون. رمزگشایی سریع است و می‌تواند هزاران رکورد را در هر ثانیه پردازش کند؛ اما رمزگذاری کندتر است، زیرا رمزگذار تمام حالت‌های نامزد را می‌سازد و کوچک‌ترین خروجی را انتخاب می‌کند. این روش جامع است، نه سریع. یک روز با فعال کردن پردازش فقط نمادهای فعال در مرحله پیمایش الفبای SymbolStats، سرعت را ۲٫۸ برابر افزایش دادم، بدون آنکه حتی یک بایت از خروجی تغییر کند. این کار با حدود ۴۰ خط کد انجام شد و یکی از رضایت‌بخش‌ترین بخش‌های آن آخر هفته بود.

جمع‌بندی

  • توکن‌ساز همان فرهنگ واژگانی است که شخص دیگری از قبل آموزش داده است. استفاده مجدد از o200k_base به‌عنوان الفبای فشرده‌سازی، مزیتی آماده و کم‌هزینه فراهم می‌کند؛ اما نباید انتظار داشت که صرف توکن‌سازی به بهبود اصلی منجر شود.
  • رکوردهای کوچک و هم‌ساختار شرایط ویژه‌ای دارند. فشرده‌سازی ۱۵۰ رکورد در یک جریان، نسبت ۰٫۰۸۷۵ ایجاد کرد؛ در حالی که پردازش جداگانه هر رکورد نسبت را به ۱٫۱۹ رساند. مزیت اصلی در شیوه بسته‌بندی و پردازش مجموعه‌ای داده‌هاست.
  • مدل‌سازی عامل اصلی است و تبدیل فقط یک ابزار کمکی است. توکن‌سازی آنتروپی را حفظ می‌کند؛ بنابراین، تمام بهبودها حاصل عملکرد واقعی کدگذار آنتروپی و فرهنگ واژگان آموزش‌دیده در پیش‌بینی ساختار داده‌ها هستند.
  • نتایج صادقانه از اعداد چشمگیر مهم‌ترند. TokPress در فشرده‌سازی متن از gzip بهتر عمل می‌کند و در برخی مجموعه‌های داده با zstd برابری می‌کند یا از آن جلو می‌زند، اما در مقایسه با zstd مجهز به فرهنگ واژگان آموزش‌دیده همچنان حدود ۱٫۳ برابر ضعیف‌تر است. همه این نتایج اندازه‌گیری شده‌اند و حتی بخش‌هایی که عملکرد ضعیف‌تری داشتند، درس‌های مهمی به همراه آوردند.

اگر می‌خواهید پروژه را بررسی یا آزمایش کنید، کد آن به‌صورت متن‌باز در مخزن GitHub پروژه TokPress در دسترس است. اسکریپت ارزیابی scripts/bench.py مرجع تمام اعداد گزارش‌شده در این مقاله است و نسبت‌های فشرده‌سازی را تنها پس از بررسی بازسازی دقیق داده‌ها گزارش می‌کند. آزمایش این ابزار روی گزارش‌های شما نیز می‌تواند نتایج جالبی به همراه داشته باشد.

منابع

مقاله‌ها و پژوهش‌هایی که هنگام توسعه این ابزار بارها به آن‌ها رجوع کردم، به سه موضوع اصلی مربوط می‌شوند: کدگذاری آنتروپی، توکن‌سازی و این ایده که فشرده‌سازی در اصل نوعی پیش‌بینی است.

  1. J. Duda، Asymmetric Numeral Systems، مقاله arXiv:0902.0271. این پژوهش مبنای کدگذار rANS است؛ مرحله آنتروپی در TokPress پیاده‌سازی مستقیمی از گونه مبتنی بر بازه این روش است.
  2. J. Ziv و A. Lempel، Compression of Individual Sequences via Variable-Rate Coding، مجله IEEE Transactions on Information Theory، جلد ۲۴، شماره ۵، سال ۱۹۷۸. این اثر به LZ77 در سطح توکن و افزونگی ناشی از طول محدود داده‌ها مربوط است؛ مسئله‌ای که رکوردهای کوچک را به حوزه‌ای مهم برای بررسی تبدیل می‌کند.
  3. P. Gage، A New Algorithm for Data Compression، نشریه The C Users Journal، جلد ۱۲، شماره ۲، سال ۱۹۹۴. این مقاله درباره کدگذاری جفت‌بایتی است؛ روشی که از پیشینیان توکن‌سازهای زیرواژه‌ای امروزی به شمار می‌رود.
  4. A. Radford، J. Wu و همکاران، Language Models are Unsupervised Multitask Learners، OpenAI، سال ۲۰۱۹. این پژوهش به توکن‌سازی BPE در سطح بایت مربوط است که در نهایت به واژگانی مانند o200k_base انجامیده است.
  5. T. Kudo و J. Richardson، SentencePiece: A simple and language independent subword tokenizer and detokenizer for Neural Text Processing، مقاله نمایشی کنفرانس EMNLP، شناسه arXiv:1808.06226، سال ۲۰۱۸. این اثر درباره مدل‌سازی زیرواژه‌ای است؛ رویکردی که ایده استفاده از توکن‌ساز به‌عنوان فرهنگ واژگان بر آن تکیه دارد.
  6. G. Delétang، A. Ruoss و همکاران، Language Modeling Is Compression، مقاله arXiv:2309.10668، سال ۲۰۲۳. این پژوهش رابطه میان پیش‌بینی و فشرده‌سازی را بررسی می‌کند و یادآور می‌شود که خود تبدیل داده‌ها مزیت فشرده‌سازی ایجاد نمی‌کند.
  7. C. S. K. Valmeekam، K. Narayanan، D. Kalathil، J.-F. Chamberland و S. Shakkottai، LLMZip: Lossless Text Compression using Large Language Models، مقاله arXiv:2306.04050، سال ۲۰۲۳. این پژوهش به استفاده از مدل‌های زبانی بزرگ به‌عنوان پیش‌بینی‌کننده می‌پردازد؛ در حالی که TokPress در سوی کم‌هزینه‌تر این طیف، از فرهنگ واژگان ثابت استفاده می‌کند.
  8. O. Goldman، A. Caciularu، M. Eyal، K. Cao، I. Szpektor و R. Tsarfaty، Unpacking Tokenization: Evaluating Text Compression and its Correlation with Model Performance، مقاله arXiv:2403.06265، منتشرشده در مجموعه مقالات EMNLP Findings، سال ۲۰۲۴. این پژوهش مبنای قابلیت tokpress tokenize-stats است که از فشرده‌سازی به‌عنوان شاخصی برای ارزیابی کیفیت توکن‌ساز استفاده می‌کند.
  9. Y. Collet و M. Kucherawy، Zstandard Compression and the application/zstd Media Type، استاندارد RFC 8478، سال ۲۰۱۸. این استاندارد مرجع اصلی مقایسه با zstd و حالت استفاده از فرهنگ واژگان آن است؛ قابلیتی که در آزمایش‌های این پروژه همچنان عملکرد بهتری نشان می‌دهد.
  10. J. G. Cleary و I. H. Witten، Data Compression Using Adaptive Coding and Partial String Matching، مجله IEEE Transactions on Communications، جلد ۳۲، شماره ۴، سال ۱۹۸۴. این پژوهش درباره روش PPM است و ایده بازگشت از مدل مرتبه بالاتر به مدل مرتبه پایین‌تر در صورت نبود تطبیق را مطرح می‌کند؛ ایده‌ای که در حالت مرتبه یک TokPress برای هر رکورد به کار گرفته شده است.