فاز 19 · درس 42

دانلود استریمینگ و پایدار دیتاسِت‌های حجیم با MinHash و LSH

نوع
ساختنی
زمان
حدود 90 دقیقه
زبان‌ها
Python
یک خط لوله دیجیتال شفاف با فیلترهای حلقوی هندسی و چک‌پوینت‌های امنیتی که داده‌های جریانی را از میان خود عبور می‌دهند
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. مفاهیم پایه
  4. استریمینگ با urllib
  5. ادامه‌ی دانلود با Range
  6. ترکیب MinHash و LSH
  7. مانیفست شاردها به‌عنوان یک قرارداد
  8. پیاده‌سازی از صفر
  9. الگوهای محیط عملیاتی
  10. استفاده از ابزارهای واقعی
  11. انتشار در محیط عملیاتی
  12. تمرین‌ها
  13. اصطلاحات کلیدی
  14. برای مطالعه‌ی بیشتر

آموزش مدل‌های زبانی روی پیکره‌های متنی عظیم (Corpus) آغاز می‌شود؛ جایی که دانلود صدها گیگابایت داده از سرورهای دوردست اولین چالش زیرساختی است. ناپایداری ارتباط شبکه، قطع اتصال روی درصدهای بالا و خطاهای غیرمنتظره دانلود معمولاً باعث شکست فرآیند می‌شود. یک فراخوانی ساده با ابزارهای دانلودی معمولی بدون پشتیبانی درست از Range و اعتبارسنجی بایت‌ها، در عمل یا به دانلود دوباره‌ی چندصد گیگابایت داده ختم می‌شود یا به خرابی پنهانی که تا مرحله‌ی توکن‌سازی خود را نشان نمی‌دهد.

علاوه بر پایداری در دانلود، مدیریت حافظه‌ی رم و پالایش داده نیز ضرورت دارد. باز کردن شاردها (Shards) و نگهداری فایل‌های چند گیگابایتی روی رم ناممکن است؛ بنابراین خواندن و باز کردن فشرده‌سازی باید به‌شکل استریمینگ (Streaming) و تک‌خطی انجام شود. همچنین وجود داده‌های تقریباً تکراری (Near-duplicates) بهینگی یادگیری را کاهش داده و منابع پردازشی گران‌بها را هدر می‌دهد.

در این درس یاد می‌گیرید چطور با استفاده از کتابخانه‌ی استاندارد پایتون، سازوکار ادامه‌ی دانلود (Resume) ضدخرابی مبتنی بر Range و چک‌پوینت‌های هش پیاده کنید. سپس شاردها را با Zstandard بی‌درنگ فشرده‌زدایی کرده و با تکنیک‌های MinHash و LSH داده‌های تکراری را پیش از توکن‌سازی فیلتر نمایید.

آموزش مدل زبانی خیلی پیش از نخستین گذر رو به جلو (forward pass) آغاز می‌شود. پیکره‌ی متنی (corpus) باید روی دیسک بنشیند، از حالت فشرده خارج شود، داده‌های تکراری‌اش حذف گردد و آدرس‌پذیر باشد؛ آن هم در شرایطی که سازوکار ادامه‌ی دانلود (resume) از قبل آماده شده باشد، نه بعد از آنکه اتصال شبکه روی 4 درصد قطع شد. در این درس یک دانلودکننده‌ی استریمینگ می‌سازیم که شاردها (shard)ی فشرده را دریافت می‌کند، آن‌ها را در لحظه با Zstandard از حالت فشرده خارج می‌سازد، نمونه‌های تقریباً تکراری (near-duplicates) را با MinHash به‌همراه درهم‌سازی حساس به مکان (LSH) انگشت‌نگاری و شناسایی می‌کند و در نهایت مانیفستی از شاردها می‌نویسد که ادامه‌ی پایپ‌لاین بتواند با خیال راحت به آن اعتماد کند.

اهداف یادگیری

  • استریم شاردهای راه‌دور با urllib و خروج از حالت فشرده با zstandard بدون نگه داشتن کل فایل در حافظه.
  • ادامه‌ی دانلودهای نیمه‌کاره با ارسال درخواست‌های Range در پروتکل HTTP بر مبنای آفست بایتیِ اعتبارسنجی‌شده.
  • ساخت یک امضای MinHash برای هر سند و دسته‌بندی آن با LSH تا اسنادِ تقریباً تکراری برخورد (collision) پیدا کنند.
  • صدور مانیفست شاردها شامل هش محتوا، حجم بر حسب بایت، تعداد اسناد و وضعیت حذف تکرارها (dedup).

صورت مسئله

نخستین باری که مدلی را روی پیکره‌ای 200 گیگابایتی آموزش می‌دهید، اتصال شبکه روی 41 درصد قطع می‌شود و اسکریپت با خطای urllib بیرون می‌پرد. دفعه‌ی دوم روی 78 درصد قطع می‌شود. تا رسیدن به 99 درصد هم احتمالاً حلقه‌ی دانلود را سه بار از نو نوشته‌اید. دو مشکلی که باید از همان دقیقه‌ی اول برایشان چاره‌اندیشی کنید، ادامه‌ی دانلودهای نیمه‌کاره و حذف اسناد تکراری است. هر دو راه‌حل‌های شناخته‌شده‌ای دارند؛ اما معمولاً نادیده گرفته می‌شوند، چون پایپ‌لاین کارش را با یک فراخوانی تک‌خطیِ requests.get شروع کرده و به‌مرور دست‌وپاگیر و دردسرساز شده است.

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

حذف تکرارها نیز مسئله‌ای از جنس امضاست. حذف تکرار با هش دقیق (Exact-hash) مواردِ تقریباً تکراری را از دست می‌دهد: یک مقاله‌ی ویکی‌پدیا با سه پانویس متنی متفاوت ظاهر می‌شود، یک فایل کد با سرآیند متفاوتی از مجوز انتشار می‌آید، یا یک پست وبلاگ با پارامترهای رهگیری متفاوتی روی هر پیوند تکرار می‌شود. ترکیب MinHash و LSH این موارد را با هزینه‌ای زیرخطی (sub-linear) به دام می‌اندازد. این هزینه برابر است با یک امضا برای هر سند و یک جست‌وجوی سطل (bucket) به‌ازای هر امضا.

مفاهیم پایه

عدم تطابقتطابقجدیدتقریباً تکراریURLهای شارد در مخزن راه‌دوربرنامه‌ریزی مدخل‌های مانیفستاستریمینگ GET با Rangeهش کردن بایت‌های جزئیارسال درخواست Rangeاستریمینگ از طریق zstdپیمایش اسناد JSONLامضای MinHashجستجو در باکت LSHافزودن به شاردحذف با ثبت رأیبه‌روزرسانی مدخل مانیفستمانیفست شارد صادر شد

استریمینگ با urllib

تابع urllib.request.urlopen از کتابخانه‌ی استاندارد پایتون، شیئی شبیه به فایل (file-like) برمی‌گرداند. اگر آن را داخل zstandard.ZstdDecompressor().stream_reader قرار دهید، بایت‌ها بدون اینکه کل شارد فشرده یا بازشده روی رم ذخیره شوند، از شبکه مستقیماً وارد موتور فشرده‌زدایی و سپس تکرارکننده‌ی اسناد (document iterator) می‌شوند. تنها هزینه‌ی رم در این حالت، بافر خط، امضای MinHash برای سندِ جاری و نمایه‌ی LSH است.

ادامه‌ی دانلود با Range

دانلودکننده برای هر شارد دو فایل روی دیسک می‌نویسد: خودِ شارد و یک چک‌پوینت با پسوند .partial.json. این چک‌پوینت مقادیر verified_bytes، expected_size، sha256_prefix (محاسبه‌شده روی نخستین بایت‌های معادل verified_bytes) و آدرس URL مبدا را ذخیره می‌کند. دانلودکننده هنگام شروع به کار، چک‌پوینت را می‌خواند، sha256_prefix را روی بایت‌های ذخیره‌شده روی دیسک بازآزمایی می‌کند و تنها در صورتی دانلود را پی می‌گیرد که هشِ بازتولیدشده کاملاً همخوانی داشته باشد. اگر هش مغایرت داشته باشد، فایل ناقص دور ریخته می‌شود و دانلود از بایت صفر از سر گرفته خواهد شد. با این رویکرد خرابی خاموش ناممکن می‌شود؛ چون بایت‌های تاییدشده سنجیده می‌شوند، نه اینکه صرفاً درست فرض شوند.

ترکیب MinHash و LSH

الگوریتم MinHash شباهت جاکارد (Jaccard similarity) دو مجموعه را در فضایی ثابت تخمین می‌زند. مجموعه‌ی مربوط به هر سند، همان شینگل‌ها (shingles - n-gramهای هم‌پوشانِ) متن آن هستند. امضای نهایی شامل k کمینه‌ی مقدار هش است که هر کدام از یک تابع هش مستقل به دست می‌آیند. دو سند با شباهت جاکارد s، با احتمال s روی هر کدام از مولفه‌های امضا مقدار یکسانی دارند.

سپس LSH این k مولفه را در b باند که هر یک شامل r سطر است دسته‌بندی می‌کند، به‌طوری که k = b * r. دو سند با احتمال 1 - (1 - s^r)^b در دست‌کم یک باند با هم برخورد دارند؛ این رابطه یک آستانه‌ی قطعی و تیز حول مقدار s می‌سازد که می‌توانید با تنظیم (b, r) آن را کنترل کنید. آستانه‌ی متداول برای حذف تکرارها در پیکره‌های متنی s = 0.8 است که در مقالات پژوهشی LSH با مقادیر k = 128، b = 32 و r = 4 حاصل می‌شود.

مانیفست شاردها به‌عنوان یک قرارداد

تنها خروجی پایدارِ دانلودکننده، مانیفست است. مانیفست به‌ازای هر شارد شامل آدرس URL، حجم بایت‌های بازشده، تعداد اسناد، تعداد اسناد یکتا پس از حذف تکرارها و هش sha256 فایل نهایی شارد است. بخش‌های بعدی توکن‌سازی، مانیفست را مبنای کار قرار می‌دهند نه فهرست فایل‌های دایرکتوری را. اگر شاردی موجود نباشد یا sha256 آن اشکال داشته باشد، مانیفست به مرحله‌ی بعد اعلام می‌کند که اجرا نشود. این مانیفست همان مرز تعیین‌کننده‌ای است که «داده دانلود شده» را از «داده دانلود و اعتبارسنجی شده» جدا می‌سازد.

دانلودکننده مجموعه‌دادهجریان · ادامه دانلود · حذف تکرار
CORPUS DOWNLOADERShards stream from the remote source and decompress through Zstandard without buffering the whole file. A verified byte offset lets a dropped connection resume with an HTTP Range request, and each document gets a MinHash signature bucketed by LSH so near-duplicates collide and drop before reaching the manifest.راه‌دورشاردهاادامه با RangezstdجریانLSH 1LSH 2LSH 3تکراری حذف شد
شاردها بدون بافر کردن کل فایل، از مخزن راه‌دور به صورت جریانی دریافت و با Zstandard فشرده‌زدایی می‌شوند. یک آفست بایتی تاییدشده امکان ادامه دانلود قطعی اتصال را با درخواست HTTP Range فراهم می‌کند، و برای هر سند یک امضای MinHash در سطل‌های LSH قرار می‌گیرد تا موارد بسیار مشابه، پیش از رسیدن به مانیفست با هم برخورد کرده و حذف شوند.

پیاده‌سازی از صفر

فایل code/main.py این بخش‌ها را پیاده‌سازی می‌کند:

  • ShardPlanner - فهرستی از URLهای شاردها را می‌خواند و ورودی‌های برنامه‌ریزی‌شده‌ی مانیفست را می‌سازد.
  • StreamingDownloader - استریم urllib را با قابلیت اختیاری Range باز می‌کند، در فایلی موقت می‌نویسد، چک‌پوینت .partial.json را با دریافت هر چانک به‌روزرسانی می‌کند و پیشوند sha256 را هنگام ادامه‌ی دانلود اعتبارسنجی می‌نماید.
  • ZstdDocIterator - استریم فایلی را درون zstandard.ZstdDecompressor می‌پیچد و در هر خط، یک سند برمی‌گرداند.
  • MinHasher - امضایی با k مولفه برای یک رشته با استفاده از خانواده‌ای ثابت از مقادیر سید هش (hash seeds) تولید می‌کند.
  • LSHIndex - امضاها را بر اساس باند دسته‌بندی کرده و موارد هم‌پوشان را گزارش می‌دهد.
  • Dedup - ترکیب هش‌ساز و نمایه برای برچسب‌گذاری هر سند با عنوان keep (نگه‌داری) یا near_duplicate (تقریباً تکراری) به همراه شناسه‌ی شاردِ هم‌پوشان.
  • ManifestWriter - آمارهای مربوط به هر شارد را گردآوری کرده و فایل manifest.json را روی دیسک می‌نویسد.

یک دموی نمونه در انتهای فایل، پیکره‌ای ساختگی و کوچک روی دیسک می‌سازد، آن را با zstandard فشرده می‌کند، از طریق یک آدرس file:// دانلودش می‌نماید، تکرارهایش را حذف می‌کند و مانیفست را به نمایش درمی‌آورد.

برای اجرا:

python3 code/main.py

اسکریپت با کد خروج صفر به پایان می‌رسد و خلاصه‌ای از مانیفست را نمایش می‌دهد.

الگوهای محیط عملیاتی

چهار الگو این درس را برای کار با پیکره‌های متنی بزرگ در محیط عملیاتی (پروداکشن) آماده می‌سازد.

ذخیره‌ی چک‌پوینت پیش از نوشتن داده. فایل .partial.json باید پیش از الحاق بایت‌ها به شارد، حتماً fsync شود. در غیر این صورت، قطع ناگهانی برق ترتیب را معکوس می‌کند: بایت‌های شارد روی دیسک نوشته شده‌اند اما چک‌پوینت ثبتشان نکرده است؛ در نتیجه، اجرای بعدی تصور می‌کند بایت‌های تاییدشده‌ی کمتری در اختیار دارد و بایت‌های تکراریِ ناشی از ادامه‌ی دانلود، فایل را خراب می‌کنند. اول چک‌پوینت، سپس نوشتن داده؛ این قاعده درست مانند اصول کار با لاگ پیش‌نویس (write-ahead log) است.

نمایه‌ی LSH شاردبندی‌شده. یک نمایه‌ی LSH یکپارچه برای کل پیکره، در مقیاس 200 گیگابایت در رم جا نمی‌شود. نمایه‌ی LSH را بر اساس هش اولین باند افراز (partition) کنید، افرازها را روی دیسک نگه دارید و تنها افرازی را بخوانید که امضای جدید قرار است در آن بنشیند. هزینه‌ی این کار یک‌بار خواندن اضافی دیسک به‌ازای هر سند است؛ مزیتش این است که نمایه‌ی LSH دیگر سقف حافظه‌ی رم را پر نمی‌کند.

سنگ مزار به جای حذف فیزیکی (Tombstone). اسناد تکراریِ کنارگذاشته‌شده، در مانیفست با وضعیت near_duplicate و با ذکر شناسه‌ی شارد سندی که با آن برخورد داشته‌اند ثبت می‌شوند. پاک کردن کامل آن‌ها پیوند میان نسخه‌ی تکراری و نسخه‌ی نگه‌داری‌شده را از بین می‌برد. استفاده از الگوی tombstone امکان حسابرسی (audit trail) را حفظ می‌کند و به مراحل بعدی اجازه می‌دهد در صورت نیاز آستانه‌ی شباهت را تغییر دهند.

هش sha256 به‌ازای هر شارد، به‌همراه هش خودِ مانیفست. خودِ مانیفست نیز یک هش محتوا دریافت می‌کند. مراحل بعدی پایپ‌لاین پیش از اعتماد به داده‌های هر شارد، هش مانیفست را ارزیابی می‌کنند. بدون این کار، مانیفست به یک سطح حمله (attack surface) خاموش تبدیل می‌شود: نفوذگری که بتواند یک فایل را دست‌کاری کند، کل پایپ‌لاین را آلوده خواهد کرد.

استفاده از ابزارهای واقعی

الگوهای محیط عملیاتی:

  • امکان ادامه‌ی دانلود در هر اجرای CI. رانرهای CI موقتی هستند. دانلودکننده باید فرض کند در هر بار اجرا با دیسکی تازه روبه‌روست و داده‌ها را از کش یا مخزن راه‌دور بازگرداند. فلگ --cache-dir نقشی کلیدی دارد.
  • حذف تکرارها پیش از توکن‌سازی. توکن‌سازی پرهزینه است. دو بار اجرا کردن آن روی یک سند، بدون هیچ تغییری در نمودار تابع زیان (loss)، هزینه‌ی پردازش را دو برابر می‌کند. حذف تکرارها در بالادستِ پایپ‌لاین توکن‌سازی قرار دارد، نه در پایین‌دست آن.
  • مانیفست به‌مثابه دروازه‌ی merge. فرایند آموزش، sha256 مانیفست را از روی یک commit مشخص و ثابت می‌خواند. تغییر نسخه‌ی دیتاست نیازمند یک commit جدید برای مانیفست است. پیوند میان کد و داده از طریق git برقرار می‌شود، نه توافقات شفاهی.

انتشار در محیط عملیاتی

فایل outputs/skill-corpus-downloader.md در یک پروژه‌ی واقعی مشخص می‌کند کدام URLها به دانلودکننده متصل می‌شوند، دایرکتوری چک‌پوینت چگونه چیده شده است، عرض شینگل و سه‌گانه‌ی (k, b, r) برای حذف تکرارها چه مقادیری دارند و مانیفست در کجای سیستم کنترل نسخه نگه‌داری می‌شود. این درس موتور اصلی آن را آماده می‌کند.

تمرین‌ها

  1. فلگ --shingle-width را اضافه کنید و تغییرات وضعیت حذف تکرارها را در مقادیر 3، 5 و 9 بسنجید. از مقدار پیش‌فرضی که برمی‌گزینید دفاع کنید.
  2. با بررسی بایت‌های جادویی (magic bytes)، پشتیبانی از gzip را در کنار zstd پیاده کنید. دانلودکننده نباید نیازی به تعیین دستی نوع کدک توسط کاربر داشته باشد.
  3. حالتی تحت عنوان --resume-only پیاده کنید که اگر چک‌پوینتی نیافت، از شروع یک دانلود تازه سر باز زند. این قابلیت در CI مفید است تا مانع شود که یک اجرا تصادفاً 200 گیگابایت داده را از نو دریافت کند.
  4. نمایه‌ی LSH را به یک فایل shelf یا sqlite انتقال دهید و نرخ پردازش (throughput) آن را در مقایسه با نسخه‌ی درون‌حافظه‌ای اندازه بگیرید.
  5. اعتبارسنجی sha256 مانیفست را هنگام شروع به کار اضافه کنید. اگر مانیفست موجود روی دیسک با هش ثبت‌شده در manifest.lock یکسان نبود، دانلودکننده باید متوقف شود و کار را پیش نبرد (fail closed).

اصطلاحات کلیدی

اصطلاح در گفت‌وگوهای عامیانه معنای دقیق فنی
Shard (شارد) «یک فایل» بخشی مستقل از پیکره‌ی متنی با sha256 اختصاصی که مبنای ادامه‌ی دانلود و حذف تکرارها قرار می‌گیرد
MinHash signature (امضای مین‌هش) «اثرانگشت» طرحی فشرده (sketch) از یک مجموعه با k مولفه، که در آن هر مولفه برابر با کمینه‌ی یک تابع هش مستقل روی مجموعه است
LSH band (باند LSH) «سطل» گروهی متشکل از r مولفه‌ی امضا که به‌عنوان کلید یک سطل واحد برای شناسایی برخوردها استفاده می‌شود
Verified bytes (بایت‌های تاییدشده) «آفست ادامه‌ی دانلود» بایت‌هایی روی دیسک که پیشوند sha256 آن‌ها با چک‌پوینت بخواند؛ تنها آفست امن برای ادامه‌ی فرایند دانلود
Manifest (مانیفست) «ایندکس» تنها سند پایدار از خروجی‌های دانلودکننده که هش محتوای بخش‌ها را نیز در بر دارد

برای مطالعه‌ی بیشتر

  • RFC 7233 - درخواست‌های Range در HTTP، پروتکل ادامه‌ی دانلود
  • مشخصات قالب Zstandard - قالب فریم که خروج امن از حالت فشرده به‌صورت استریمینگ را ممکن می‌سازد
  • MinHash - خانواده‌ی امضایی که در این درس استفاده شده است
  • درهم‌سازی حساس به مکان (LSH) - طرح دسته‌بندی باندی پشت آستانه‌ی حذف تکرارها
  • بخش 19 · 43 - پیکره‌ی توکن‌سازی‌شده‌ی HDF5 که ورودی‌اش را از این دانلودکننده می‌گیرد
  • بخش 19 · 44 - برنامه‌ی زمان‌بندی کسینوسی (cosine schedule) که روی این پیکره آموزش می‌بیند
  • بخش 19 · 45 - حلقه‌ی AMP که از این برنامه‌ی زمان‌بندی استفاده می‌کند

خودآزمایی

به سؤال‌ها جواب بدهید تا ببینید چقدر از درس در ذهنتان مانده است.

  1. پیش از درسچرا دانلودکننده یک چک‌پوینت جداگانه .partial.json برای هر شارد نگه می‌دارد؟
  2. حین درسطرح خوشه‌بندی LSH چه مزیتی دارد که حذف تکرار با هش دقیق ندارد؟
  3. حین درسچرا چک‌پوینت قبل از الحاق بایت‌ها نوشته می‌شود، نه بعد از آن؟
  4. حین درسنقش فایل قفل sha256 مانیفست چیست؟
  5. پس از درسچرا حذف تکرار در بالادستِ توکن‌سازی قرار می‌گیرد نه پس از آن؟
  6. پس از درسثبت سنگ‌نشان (tombstoning) برای تکرارهای حذف‌شده چه چیزی را حفظ می‌کند که حذف خاموش فاقد آن است؟

سؤالات متداول

قابلیت Resume چگونه از فساد خاموش بایت‌ها هنگام قطع شبکه جلوگیری می‌کند؟

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

تفاوت MinHash با هش دقیق (Exact-hash) در حذف اسناد تکراری چیست؟

هش دقیق فقط اسناد کاملاً یکسان را می‌یابد و تغییرات کوچکی مثل فوترها یا پارامترهای لینک را نادیده می‌گیرد؛ اما MinHash به همراه LSH شباهت جاکارد اسناد را تخمین زده و موارد تقریباً تکراری (Near-duplicates) را با هزینه محاسباتی کم به دام می‌اندازد.

چرا حذف تکرارها (Dedup) باید قبل از توکن‌سازی انجام شود؟

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

ما رو توی گوگل بیشتر ببین

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