یادگیری ماشین در زبان برنامهنویسی سلام با بستههایی انجام میشود که همراه خود کامپایلر میآیند. کتابخانهی استاندارد سلام تانسورهای چندبعدی، مشتقگیری خودکار معکوس (Autograd)، لایههای شبکهی عصبی، بهینهسازها، مدلهای کلاسیک یادگیری ماشین، یک کتابخانهی ماتریس ۶۴ بیتی و خوانندههایی برای پروندههای مدل ONNX و GGUF دارد؛ همه به خود سلام نوشته شدهاند و بدون پایتون و بدون هیچ محیط اجرای بیرونی به کد بومی کامپایل میشوند. میتوانید درون یک برنامهی معمولی سلام یک شبکهی کوچک آموزش دهید، یک درخت تصمیم برازش کنید، یک دستگاه معادلات خطی حل کنید یا یک مدل ONNX صادرشده را اجرا کنید.
این مقاله با برنامههای کامل و خروجی واقعیشان در این پشته گشت میزند، توضیح میدهد قطعهها چطور کنار هم قرار میگیرند، و دربارهی محدودیتها دقیق است، چون یک کتابخانهی یادگیری ماشین فقط وقتی به کار میآید که بدانید کجا متوقف میشود. همهی نمونهها به سلام فارسی نوشته شدهاند؛ نام فارسی هر بسته و تابع در صفحهی مرجعش آمده است.
بستههای یادگیری ماشین سلام در یک نگاه
| بسته | کاربرد |
|---|---|
ai.tensor (هوش مصنوعی.تانسور) | تانسورهای f32، حساب با پخش (broadcasting)، ضرب ماتریسی، کاهشها، ماسکها |
ai.autograd (هوش مصنوعی.مشتقگیری خودکار) | نواری که عملیات را ثبت میکند و گرادیانها را رو به عقب حساب میکند |
ai.nn (هوش مصنوعی.شبکه عصبی) | لایهها: خطی، کانولوشن، تجمیع (pooling)، نرمالسازی، جاسازی (embedding)، LSTM، GRU، توجه (attention)، بلوکهای ترنسفورمر، نقطهی ذخیره |
ai.optim (هوش مصنوعی.بهینهسازی) | SGD با تکانه، Adam و AdamW، RMSProp، بریدن گرادیان، زمانبندی نرخ یادگیری |
ai.ml (هوش مصنوعی.یادگیری ماشین) | مدلهای کلاسیک به سبک scikit-learn: رگرسیون، درخت، جنگل، تقویت (boosting)، SVM، k-means، PCA، معیارها |
matrix (ماتریس) | جبر خطی با f64: حل دستگاه، تجزیهها، مقدارهای ویژه، SVD |
ai.onnx | بارگذاری و اجرای مدلهای ONNX صادرشده از چارچوبهای دیگر |
ai.gguf، ai.llm | خواندن پروندههای GGUF و اجرای مدلهای زبانی کوچک خانوادهی Llama روی پردازنده |
stats، data، npy | آمار توصیفی، بارگذاری و دستهبندی داده، پروندههای .npy و .npz نامپای (NumPy) |
طراحی لایهلایه است: tensor حساب ساده است، autograd عملیات روی تانسورها را ثبت میکند، nn لایهها را روی نوار میسازد و optim وزنهایی را که nn نگه میدارد بهروز میکند. اگر PyTorch را میشناسید، این ساختار برایتان آشناست؛ اگر scikit-learn را میشناسید، ai.ml آشنا خواهد بود.
تانسور و پخش (Broadcasting)
تانسور آرایهای چندبعدی از عددهای اعشاری ۳۲ بیتی است که سطربهسطر ذخیره میشود. عملگرهای +، -، * و / درایهبهدرایه کار میکنند و پخش میشوند: بُعدی با اندازهی یک کش میآید تا با عملوند دیگر هماندازه شود. این برنامه یک لایهی چگال را دستی حساب میکند: ورودی 2x3 ضرب در ماتریس وزن 3x2 بهعلاوهی بایاس 1x2 که روی هر دو سطر پخش میشود:
واردسازی هوش مصنوعی.تانسور
تابع بساز(سطرها: صحیح، ستونها: صحیح، مقدارها: اعشار۶۴[]): تانسور.تانسور:
ناپایا داده := وکتور {} برگردان وکتور<اعشار۳۲>
هر م در مقدارها:
داده.بیفزا(م برگردان اعشار۳۲)
پایان
ناپایا شکل := تانسور.شکل دو(سطرها، ستونها)
برگشت تانسور.از آرایه(شکل، داده)
پایان
تابع ریشه:
ناپایا ایکس := بساز(۲، ۳، [۱.۰، ۲.۰، ۳.۰، ۴.۰، ۵.۰، ۶.۰])
ناپایا وزن := بساز(۳، ۲، [۰.۵، -۱.۰، ۰.۲۵، ۰.۰، ۱.۰، ۲.۰])
بایاس := بساز(۱، ۲، [۱.۰، -۹.۰])
ناپایا خروجی := تانسور.ضرب ماتریسی(ایکس، وزن) + بایاس
سرچاپ "خروجی"، تانسور.رشته شکل(خروجی)، تانسور.به رشته(خروجی)
سرچاپ "یکسوساز"، تانسور.به رشته(تانسور.یکسوساز(خروجی))
سرچاپ "جمع سطرها"، تانسور.به رشته(تانسور.مجموع محور(خروجی، ۱، نادرست))
پایانعملیات بزرگ خودشان میان نخها پخش میشوند: کار درایهبهدرایه از ۲۶۲٬۱۴۴ مقدار به بالا و ضرب ماتریسی از حدود ۳۲ میلیون ضربوجمع به بالا، با یک نخ برای هر هسته و بهطور پیشفرض حداکثر ۸ نخ. SetThreads(1) همهچیز را روی نخ فراخواننده نگه میدارد.
ناسازگاری شکلها برنامه را متوقف نمیکند. نتیجه یک تانسور خالی است و ShapeString (در فارسی رشته شکل) کلمهی empty را چاپ میکند. تا وقتی از شکلها مطمئن نیستید is_empty() را بررسی کنید، همانطور که در NumPy مراقب غافلگیریهای بیصدای پخش هستید.
مشتقگیری خودکار با نوار
آموزش یعنی پایین آوردن تابع زیان (Loss) با دنبال کردن گرادیانش. ai.autograd هر عملیات را روی یک نوار (Tape) ثبت میکند و پسانتشار (Backward) نوار را وارونه میپیماید تا گرادیان زیان را نسبت به هر ورودی حساب کند. تابعها شمارهی گره برمیگردانند؛ مقدار و گرادیان تانسورهای پشت آن گرهها را میخوانند.
کوچکترین نمونهی مفید، کاهش گرادیان روی یک عدد است. زیان مربع (w - 3) است، پس گرادیان 2(w - 3) است و کمینه در ۳ قرار دارد:
واردسازی هوش مصنوعی.مشتقگیری خودکار
واردسازی هوش مصنوعی.تانسور
واردسازی تبدیل
تابع ریشه:
ناپایا نوار := مشتقگیری خودکار.نوار جدید(۱)
ناپایا آغاز := وکتور {} برگردان وکتور<اعشار۳۲>
آغاز.بیفزا(۰.۰ برگردان اعشار۳۲)
ناپایا شکل := تانسور.شکل یک(۱)
ناپایا وزن := تانسور.از آرایه(شکل، آغاز)
تکرار ۳۱ در گام:
نوار.بازنشانی()
ایکس := مشتقگیری خودکار.پایش(نوار، وزن)
فاصله := مشتقگیری خودکار.جمع با عدد(نوار، ایکس، -۳.۰)
زیان := مشتقگیری خودکار.مجموع(نوار، مشتقگیری خودکار.ضرب درایهای(نوار، فاصله، فاصله))
مشتقگیری خودکار.پسانتشار(نوار، زیان)
اگر گام % ۱۰ == ۰:
مقدار زیان := مشتقگیری خودکار.مقدار(نوار، زیان).عنصر(۰) برگردان اعشار۶۴
سرچاپ "گام"، گام، "وزن"، تبدیل.قالب اعشار با دقت(وزن.عنصر(۰) برگردان اعشار۶۴، ۴)، "زیان"، تبدیل.قالب اعشار با دقت(مقدار زیان، ۶)
پایان
ناپایا گرادیان := مشتقگیری خودکار.گرادیان(نوار، ایکس)
تانسور.جمع مقیاسدار(وزن، گرادیان، -۰.۱)
پایان
نوار.آزادسازی()
پایانبا نرخ یادگیری ۰٫۱، فاصله تا کمینه در هر گام ۰٫۸ برابر میشود، پس پس از ده گام وزن برابر 3(1 - 0.8^10) است، یعنی حدود ۲٫۶۷۸، و این دقیقاً همان چیزی است که برنامه چاپ میکند. دو قاعده از مستندات بسته در کد واقعی اهمیت دارد: در آغاز هر گام بازنشانی (reset) را صدا بزنید که تانسورهای گام قبل را آزاد میکند، و گرادیانها را صفر کنید، چون گرادیانها جمع میشوند و هرگز جایگزین نمیشوند. وقتی لایه یا تابع زیان تازهای مینویسید، GradCheck گرادیان را با یک تخمین عددی مقایسه میکند.
آموزش شبکهی عصبی با زبان سلام
ai.nn لایهها را فراهم میکند که همه وزنهایشان را در یک مخزن پارامتر (ParamStore) نگه میدارند، و ai.optim قاعدهی بهروزرسانی را. XOR آزمون کلاسیک اول است، چون هیچ خط راستی دو دستهاش را از هم جدا نمیکند و به یک لایهی پنهان نیاز است:
واردسازی هوش مصنوعی.شبکه عصبی
واردسازی هوش مصنوعی.مشتقگیری خودکار
واردسازی هوش مصنوعی.بهینهسازی
واردسازی هوش مصنوعی.تانسور
تابع جدول(سطرها: صحیح، ستونها: صحیح، مقدارها: اعشار۶۴[]): تانسور.تانسور:
ناپایا داده := وکتور {} برگردان وکتور<اعشار۳۲>
هر م در مقدارها:
داده.بیفزا(م برگردان اعشار۳۲)
پایان
ناپایا شکل := تانسور.شکل دو(سطرها، ستونها)
برگشت تانسور.از آرایه(شکل، داده)
پایان
تابع ریشه:
ناپایا ایکس := جدول(۴، ۲، [۰.۰، ۰.۰، ۰.۰، ۱.۰، ۱.۰، ۰.۰، ۱.۰، ۱.۰])
ناپایا هدف := جدول(۴، ۱، [۰.۰، ۱.۰، ۱.۰، ۰.۰])
ناپایا مخزن := شبکه عصبی.مخزن پارامتر {}
ناپایا مولد := تانسور.مولد تصادفی جدید(۴۲)
ناپایا مدل := شبکه عصبی.ترتیبی {}
مدل.افزودن(شبکه عصبی.لایه خطی جدید(مخزن، "پنهان"، ۲، ۱۶، مولد))
مدل.افزودن(شبکه عصبی.لایه یکسوساز {})
مدل.افزودن(شبکه عصبی.لایه خطی جدید(مخزن، "خروجی"، ۱۶، ۱، مولد))
ناپایا بهینهساز := بهینهسازی.آدام { نرخ یادگیری = ۰.۰۳ }
ناپایا نوار := مشتقگیری خودکار.نوار جدید(۱)
تکرار ۶۰۰ در گام:
نوار.بازنشانی()
مخزن.صفر کردن گرادیان()
لوجیت := مدل.پیشرو(نوار، مخزن، مشتقگیری خودکار.ورودی(نوار، ایکس))
زیان := مشتقگیری خودکار.آنتروپی دودویی لوجیت(نوار، لوجیت، مشتقگیری خودکار.ثابت(نوار، هدف))
مشتقگیری خودکار.پسانتشار(نوار، زیان)
بهینهساز.گام(مخزن)
اگر گام % ۲۰۰ == ۰:
سرچاپ "گام"، گام، "زیان کمتر از ۰٫۱:"، مشتقگیری خودکار.مقدار(نوار، زیان).عنصر(۰) < ۰.۱
پایان
پایان
نوار.بازنشانی()
احتمال := تانسور.سیگموئید(مشتقگیری خودکار.مقدار(نوار، مدل.پیشرو(نوار، مخزن، مشتقگیری خودکار.ورودی(نوار، ایکس))))
تکرار ۴ در ای:
سرچاپ ایکس.در دو(ای، ۰)، "xor"، ایکس.در دو(ای، ۱)، "->"، (احتمال.عنصر(ای) + ۰.۵) برگردان صحیح
پایان
نوار.آزادسازی()
بهینهساز.آزادسازی()
مخزن.آزادسازی()
پایانحلقه در هر برنامهی آموزشی همین پنج خط است: نوار را بازنشانی کنید، گرادیانها را صفر کنید، گذر رو به جلو و زیان را اجرا کنید، پسانتشار را صدا بزنید و بهینهساز را یک گام جلو ببرید. بذری که به مولد تصادفی جدید (NewRng) داده میشود وزنهای آغازین را ثابت میکند، پس هر اجرا دقیقاً تکرار میشود. نقطههای ذخیره (Checkpoint) یک مخزن پارامتر را با نام لایهها ذخیره میکنند (اگر مسیر به .gz ختم شود با gzip فشرده میشود) و بارگذاری، پیش از نوشتن هر وزن، CRC، تعداد پارامترها، هر نام و هر شکل را بررسی میکند. به فراخوانیهای صریح آزادسازی در پایان هم دقت کنید: سلام زبالهروب ندارد، پس کارهای آموزشی طولانی باید تانسورهایی را که دیگر لازم ندارند آزاد کنند؛ مقالهی مدیریت حافظه در سلام این را شرح میدهد.
یادگیری ماشین کلاسیک با ai.ml
بسیاری از مسئلههای عملی به یک درخت تصمیم یا رگرسیون لجستیک نیاز دارند، نه به شبکهی عصبی. ai.ml از الگوی scikit-learn پیروی میکند: داده یک ماتریس است که هر سطرش یک نمونه است، هر مدل ساختاری با برازش (fit) و پیشبینی (predict) است، و معیارها، مقیاسکنندهها، تقسیمکنندهها، اعتبارسنجی متقابل و جستوجوی شبکهای هم موجودند. یک درخت کمعمق که از روی ساعت مطالعه و ساعت خواب پیشبینی میکند دانشجو قبول میشود یا نه:
واردسازی هوش مصنوعی.یادگیری ماشین
واردسازی ماتریس
تابع بساز(سطرها: صحیح، ستونها: صحیح، عددها: رشته): ماتریس.ماتریس:
ناپایا داده := وکتور {} برگردان وکتور<اعشار۶۴>
هر عدد در عددها.بشکاف(" "):
داده.بیفزا(عدد.به اعشار())
پایان
برگشت ماتریس.از آرایه(سطرها، ستونها، داده)
پایان
تابع ریشه:
ناپایا ساعتها := بساز(۱۰، ۲، "1 4 2 5 2 8 3 6 4 7 5 7 6 8 7 6 8 7 9 8")
ناپایا قبولی := بساز(۱۰، ۱، "0 0 0 0 1 1 1 1 1 1")
ناپایا درخت := یادگیری ماشین.درخت تصمیم { بیشترین عمق = ۲ }
دیرکن درخت.آزادسازی()
سرچاپ "آموزش دید:"، درخت.برازش(ساعتها، قبولی)، "گرهها:"، درخت.شمار گره()
ناپایا پیشبینی := درخت.پیشبینی(ساعتها)
سرچاپ "دقت روی دادهی آموزش:"، یادگیری ماشین.دقت(قبولی، پیشبینی)
ناپایا دانشجوها := بساز(۳، ۲، "1 9 5 5 10 4")
ناپایا حدس := درخت.پیشبینی(دانشجوها)
تکرار ۳ در ای:
نتیجه := حدس.عنصر(ای، ۰) > ۰.۵ ? "قبول" : "مردود"
سرچاپ "مطالعه"، دانشجوها.عنصر(ای، ۰)، "خواب"، دانشجوها.عنصر(ای، ۱)، "->"، نتیجه
پایان
پایاندرخت همان یک شکافی را پیدا کرد که اهمیت دارد (ساعت مطالعه) و سه گره کافی است. دقت ۱ روی ده نمونهی آموزشی هیچ چیزی دربارهی دادهی تازه ثابت نمیکند؛ برای همین بسته TrainTestSplit، StratifiedKFold و CrossValScore را هم دارد. فهرست مدلها بلند است: رگرسیون خطی، ریج، لاسو و لجستیک، درخت تصمیم، جنگل تصادفی، تقویت گرادیان و تقویت گرادیان هیستوگرامی، AdaBoost، SVM خطی و هستهای، k نزدیکترین همسایه، بیز ساده، LDA و QDA، k-means، DBSCAN، مخلوط گاوسی، خوشهبندی تجمعی، PCA، SVD بریده و t-SNE.
جبر خطی دقیق با بستهی ماتریس
تانسورها f32 هستند، چون شبکههای عصبی بیشتر به سرعت نیاز دارند تا به رقمهای آخر. مهندسی، آمار و مالی معمولاً عکس این را میخواهند، پس بستهی matrix با عددهای اعشاری ۶۴ بیتی کار میکند: Solve برای یک دستگاه، و برای چند دستگاه یک بار Decompose و چند بار LUSolve، بهعلاوهی مقدارهای ویژه، مقدارهای تکین و تحلیل مؤلفههای اصلی. این بسته هم مثل تانسورها شکست را با یک ماتریس خالی گزارش میکند و برنامه را متوقف نمیکند، پس پس از Solve روی ماتریسی که ممکن است وارونناپذیر باشد، is_empty() را بررسی کنید.
اجرای مدلهایی که جای دیگر آموزش دیدهاند: ONNX و GGUF
بیشتر تیمها با پایتون آموزش میدهند. ai.onnx به یک برنامهی سلام اجازه میدهد مدل صادرشده را بدون کتابخانهی ONNX Runtime اجرا کند: LoadOnnx پرونده را میخواند و Run تانسورهای معمولی برمیگرداند. محدودیتها صریح مستند شدهاند. حدود ۶۵ عملگر پیادهسازی شده که لایههای پیشخور، کانولوشنی و نرمالسازی را پوشش میدهد؛ LSTM، Resize و عملگرهای کوانتیزه با خطای unsupported op شکست میخورند. همهی مقدارها درون بسته f32 هستند و وزنهایی که در پروندهی دادهی بیرونی جداگانه ذخیره شدهاند بارگذاری نمیشوند.
ai.gguf قالب GGUF پروژهی llama.cpp را میخواند و ai.llm مدلهای خانوادهی Llama مثل Qwen2.5 را روی پردازنده اجرا میکند. پرونده باید کمتر از ۲ گیگابایت باشد و وزنهایش F32، F16، Q8_0، Q4_0 یا Q4_1 باشد؛ کوانتیزهسازیهای پرطرفدار K پشتیبانی نمیشوند. وزنها هنگام بارگذاری به f32 باز میشوند، پس پروندهی ۰٫۷ گیگابایتی Qwen2.5 0.5B حدود ۳ گیگابایت حافظه لازم دارد. مستندات خود بسته توصیه میکند برای مدلهای بزرگ یا سرعت بالا یک سرور استنتاج اختصاصی را صدا بزنید. مقالهی اجرای بارهای کاری هوش مصنوعی با سلام این جنبه را عمیقتر بررسی میکند.
کارایی: محک (Benchmark) چه میگوید
محک پروژه سه برنامه دارد که روی همین کتابخانهها ساخته شدهاند. این سه از میانگینها بیرون نگه داشته شدهاند، چون سلام کتابخانهی استانداردش را صدا میزند در حالی که نسخههای C، C++، Rust و Go حلقههای دستنویس دارند؛ پس این برنامهها کتابخانهها را مقایسه میکنند، نه کامپایلرها را. سلام در دو تا جلو بود: ضرب با matrix ۱۴٫۳ میلیثانیه در برابر ۴۲٫۵ میلیثانیهی C، و ضرب با tensor ۷٫۳ میلیثانیه در برابر ۳۸٫۱ میلیثانیهی C++. در سومی، مجموعهای از عملیات کوچک ماتریسی، با ۳۶٫۴ میلیثانیه در برابر ۴٫۱ میلیثانیهی C بسیار عقب بود؛ آنجا gcc بیشتر کار حلقه را حذف میکند. این را اینطور بخوانید: «کتابخانهی تنظیمشده از حلقهی ساده جلو میزند و وقتی بهینهساز بتواند کار را حذف کند عقب میماند»، نه بهعنوان رتبهبندی کلی سرعت. مقالهی سلام در برابر پایتون و Julia جنبهی عددی را با آن اکوسیستمها مقایسه میکند.
سلام برای یادگیری ماشین چه چیزی نیست
- پردازندهی گرافیکی (GPU) ندارد. همهچیز روی CPU اجرا میشود. هیچ پشتیبان CUDA، Metal یا Vulkan وجود ندارد، پس آموزش هر چیزی فراتر از مدلهای کوچک عملی نیست.
- اکوسیستم کوچک است. PyTorch، JAX، scikit-learn و Hugging Face سالها مدل، آموزش و وزن ازپیشآموخته دارند. سلام یک کتابخانهی استاندارد دارد و هیچ مجموعهی مدل شخص ثالثی ندارد. پایتون همچنان جای درست پژوهش است.
- فقط تانسور
f32. آموزش باf16یاbf16در کار نیست و وزنهای صحیح بهf32تبدیل میشوند. - حافظهی دستی. هر عملیات تانسور تازهای میسازد. در یک حلقهی طولانی از شکلهای
IntoوInPlaceتابعها استفاده کنید یا نتیجهها را صریحاً آزاد کنید. - کد جوان. این بستهها تازهاند، بیرون از خود پروژه کاربر کمی دارند و رابط برنامهنویسیشان پیش از نسخهی ۱٫۰ هنوز ممکن است تغییر کند.
جای سلام سر دیگر این زنجیره است: یک پروندهی اجرایی بومی که مدل آموزشدیده را بارگذاری میکند و آن را از راه HTTP ارائه میدهد، سطرهای یک پایگاه داده را امتیاز میدهد، یا یک دستهبند کوچک را درون یک ابزار خط فرمان جا میدهد، بدون اینکه روی ماشین مقصد محیط پایتونی نصب شود. سلام جای خوانایی هم برای یاد گرفتن طرز کار autograd و ترنسفورمرهاست، چون هر لایه کد سلام معمولی در std/ai/ است؛ و برای خوانندهی فارسیزبان، این یعنی میتواند کل زنجیره را با نامهای فارسی بنویسد و بخواند. گشت در کتابخانهی استاندارد نشان میدهد چه چیزهای دیگری کنار این بستهها هست.
پرسشهای پرتکرار دربارهی یادگیری ماشین با زبان سلام
آیا میتوان با زبان سلام شبکهی عصبی آموزش داد؟
بله. ai.nn لایههای خطی، کانولوشنی، بازگشتی، توجه و ترنسفورمر دارد، ai.autograd گرادیانها را حساب میکند و ai.optim بهینهسازهای SGD، Adam، AdamW و RMSProp را دارد. آموزش روی CPU اجرا میشود، پس برای مدلهای کوچک و متوسط مناسب است، نه مدلهای بزرگ.
آیا سلام برای یادگیری ماشین از GPU پشتیبانی میکند؟
نه. همهی عملیات تانسور روی CPU اجرا میشوند و عملیات بزرگ میان نخها تقسیم میشوند. هیچ پشتیبان CUDA یا GPU دیگری وجود ندارد.
آیا سلام میتواند مدل PyTorch را بارگذاری کند؟
مستقیم نه. مدل را به ONNX صادر کنید و با ai.onnx بارگذاری کنید، به شرط آنکه فقط از حدود ۶۵ عملگر پشتیبانیشده استفاده کند و وزنهایش درون همان پروندهی .onnx باشد. برای مدلهای زبانی، ai.llm پروندههای GGUF خانوادهی Llama را بارگذاری میکند.
آیا زبان سلام جایگزین پایتون در علم داده است؟
امروز نه. کتابخانهها، نوتبوکها و جامعهی پایتون بسیار بزرگترند. سلام وقتی به کار میآید که برنامهای کامپایلشده بدون محیط اجرای پایتون بخواهید، یا بخواهید یک پشتهی کامل یادگیری ماشین را که به یک زبان نوشته شده بخوانید و از آن یاد بگیرید.