پردازش زبان طبیعی

چالش‌های زبان فارسی در مدل‌های زبانی

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

· ۹ دقیقه مطالعه

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

این مقاله همان مشکلات را یکی‌یکی باز می‌کند: کارایی tokenization، تنوع املایی، درهم‌آمیختگی RTL و LTR، شکل ارقام، و کمبود داده‌ی ارزیابی فارسی. برای هرکدام هم می‌گوید در پردازش زبان طبیعی چه کاری عملاً می‌شود کرد.

tokenization و هزینه‌ای که مستقیماً می‌پردازید

مدل زبانی متن را کاراکتر به کاراکتر نمی‌بیند. متن اول به token شکسته می‌شود — قطعه‌هایی که ممکن است یک کلمه، بخشی از کلمه یا حتی چند بایت باشند. tokenizer روی یک پیکره‌ی آموزشی ساخته می‌شود و قطعه‌های پرتکرار همان پیکره را به یک token نگاشت می‌کند.

پیکره‌ی آموزشیِ اغلب tokenizerهای پرکاربرد به‌شدت انگلیسی‌محور است. نتیجه این است که یک کلمه‌ی رایج انگلیسی معمولاً یک token می‌شود، در حالی که یک کلمه‌ی فارسی به چند قطعه شکسته می‌شود. این نسبت بسته به مدل و tokenizer فرق می‌کند و عدد ثابتی ندارد؛ اما جهتش تقریباً همیشه یکسان است: یک متن فارسی نسبت به ترجمه‌ی انگلیسی‌اش token بیشتری مصرف می‌کند.

سه پیامد عملی دارد:

  • هزینه. قیمت‌گذاری API بر اساس token است، نه بر اساس کلمه. همان محتوا به فارسی گران‌تر تمام می‌شود.
  • پنجره‌ی زمینه. context window بر حسب token شمرده می‌شود. یعنی در یک پنجره‌ی مشخص، متن فارسی کمتری جا می‌شود.
  • تأخیر. خروجی token به token تولید می‌شود؛ پاسخ فارسی با تعداد token بیشتر، زمان بیشتری می‌برد.

کاری که می‌شود کرد: تعداد token را حدس نزنید، بشمارید. اغلب SDKها امکان شمارش token قبل از ارسال دارند. یک بار روی نمونه‌ی واقعی داده‌ی خودتان اندازه بگیرید و همان عدد را مبنای برآورد هزینه و اندازه‌ی تکه‌ها بگذارید. عددهایی که در بحث‌های عمومی نقل می‌شود تقریبی و وابسته به مدل‌اند و نباید بدون آزمایش مبنای طراحی قرار بگیرند.

تنوع املایی؛ یک کلمه، چند نوشتار

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

موردشکل درست فارسیشکل رایج نادرستکد یونیکد
ییي (عربی)U+06CC در برابر U+064A
ککك (عربی)U+06A9 در برابر U+0643
نیم‌فاصلهمی‌رودمی رود / میرودU+200C
کشیدگیکتابکـتـابU+0640
اعرابمدرسهمَدرِسهU+064BU+0652

نیم‌فاصله — یا به زبان یونیکد، zero-width non-joiner — سخت‌ترین موردشان است. کاراکتری است که عرض ندارد ولی اتصال حروف را قطع می‌کند و در فارسی معنادار است: «می‌روم» با «می روم» و «میروم» سه رشته‌ی متفاوت‌اند. کاربران هر سه را تایپ می‌کنند. سیستم‌های تولید محتوا هم گاهی آن را در خروجی حذف یا با فاصله جایگزین می‌کنند.

اثرش کجا دیده می‌شود؟ در جست‌وجوی کلیدواژه‌ای تطبیق شکست می‌خورد. در tokenization، شکل‌های مختلف به token های مختلف تبدیل می‌شوند. در deduplication، دو نسخه از یک سند تکراری تشخیص داده نمی‌شوند. در گروه‌بندی و شمارش، یک عبارت به چند دسته می‌شکند.

نرمال‌سازی به‌عنوان یک مرحله‌ی مشخص در خط لوله

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

یک نرمال‌سازی معقول برای فارسی این کارها را می‌کند:

  1. یکسان‌سازی حروف عربی به فارسی: ي به ی و ك به ک.
  2. حذف اعراب و کشیدگی، که تقریباً هیچ‌وقت اطلاعات لازمی حمل نمی‌کنند.
  3. تصمیم صریح درباره‌ی نیم‌فاصله — حفظش، تبدیلش به فاصله، یا حذفش — و اجرای همان تصمیم همه‌جا.
  4. یکسان‌سازی ارقام به یک شکل.
  5. یکسان‌سازی فاصله‌های تکراری و کاراکترهای نامرئی دیگر مثل U+200E و U+200F.
  6. یکسان‌سازی علائم: گیومه‌ها، سه‌نقطه، و ویرگول فارسی ، در برابر ویرگول لاتین.

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

کتابخانه‌های فارسی مثل hazm و parsivar این مرحله را آماده دارند. اگر از آن‌ها استفاده می‌کنید نسخه را قفل کنید، چون تغییر رفتار نرمال‌سازی بین ساخت ایندکس و زمان اجرا همان ناسازگاری را برمی‌گرداند.

متن دوجهته؛ RTL کنار LTR

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

مشکلات معمول:

  • پرانتز و علائم مرزی. نقطه، ویرگول و پرانتز در انتهای یک قطعه‌ی انگلیسیِ داخل جمله‌ی فارسی ممکن است در سمت اشتباه رندر شوند.
  • کاراکترهای کنترلی نامرئی. بعضی ابزارها U+200E و U+200F را برای اصلاح ظاهر تزریق می‌کنند. این کاراکترها در متن باقی می‌مانند، در token شمرده می‌شوند و تطبیق رشته‌ای را می‌شکنند.
  • استخراج از PDF. ترتیب کاراکترها در بخش‌های دوجهته موقع تبدیل به متن خام می‌تواند جابه‌جا شود. متن حاصل در مرورگر تقریباً درست به نظر می‌رسد و در سطح بایت خراب است.

کار عملی: قبل از ساختن هر خط لوله‌ای، چند سند نمونه را در سطح خام — نه در مرورگر — نگاه کنید. repr() بگیرید و کاراکترهای نامرئی را ببینید. اگر متن ورودی‌تان از PDF می‌آید، این مرحله را رد نکنید.

شکل ارقام

سه دسته رقم در متن فارسی دیده می‌شود: لاتین (09)، عربی (U+0660U+0669) و فارسی (U+06F0U+06F9). یک سند واحد ممکن است هر سه را داشته باشد، مخصوصاً وقتی از چند منبع سرهم شده است.

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

قاعده‌ی ساده: در لایه‌ی داده همه‌ی ارقام را به یک شکل — معمولاً لاتین — نرمال کنید و تبدیل به ارقام فارسی را فقط در لایه‌ی نمایش انجام دهید.

کمبود داده‌ی ارزیابی فارسی

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

پیامدش این است که وقتی می‌پرسید «کدام مدل برای فارسی بهتر است؟»، معمولاً پاسخ قابل اتکایی در دسترس نیست. اعداد منتشرشده‌ی عمومی هم غالباً روی وظایف انگلیسی سنجیده شده‌اند و تعمیمشان به فارسی ضمانتی ندارد.

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

بیشتر مشکلات فارسی در مدل‌های زبانی، مشکل هوش مدل نیست؛ مشکل نمایش متن و نبود داده‌ی سنجش است. هر دو حل‌شدنی‌اند و هر دو کار مهندسی می‌خواهند، نه مدل بزرگ‌تر.

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