TensorFlow.js: "ماشین قابل آموزش" با استفاده از آموزش انتقال با TensorFlow.js

1. قبل از شروع

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

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

این کد لبه به شما نشان می دهد که چگونه می توانید یک برنامه وب را از روی بوم خالی بسازید و وب سایت محبوب Google Teachable Machine را بازسازی کنید. این وب‌سایت به شما امکان می‌دهد یک برنامه وب کاربردی ایجاد کنید که هر کاربر می‌تواند از آن برای تشخیص یک شی سفارشی فقط با چند نمونه تصویر از وب‌کم خود استفاده کند. این وب‌سایت عمداً به حداقل می‌رسد تا بتوانید بر جنبه‌های یادگیری ماشینی این کد لبه تمرکز کنید. با این حال، مانند وب‌سایت اصلی Teachable Machine، فضای زیادی برای اعمال تجربه توسعه‌دهنده وب موجود برای بهبود UX وجود دارد.

پیش نیازها

این کد لبه برای توسعه دهندگان وب نوشته شده است که تا حدودی با مدل های از پیش ساخته شده TensorFlow.js و استفاده اولیه از API آشنا هستند و می خواهند با آموزش انتقال inTensorFlow.js شروع کنند.

  • آشنایی اولیه با TensorFlow.js، HTML5، CSS و JavaScript برای این آزمایشگاه فرض شده است.

اگر با Tensorflow.js تازه کار هستید، ابتدا این دوره رایگان از صفر تا قهرمان را بگذرانید ، که در آن هیچ پیش زمینه ای در زمینه Machine Learning یا TensorFlow.js وجود ندارد و هر آنچه را که باید در مراحل کوچکتر بدانید به شما آموزش می دهد.

چیزی که یاد خواهید گرفت

  • TensorFlow.js چیست و چرا باید از آن در برنامه وب بعدی خود استفاده کنید.
  • چگونه یک صفحه وب HTML/CSS/JS ساده بسازیم که تجربه کاربر Teachable Machine را تکرار کند.
  • نحوه استفاده از TensorFlow.js برای بارگذاری یک مدل پایه از پیش آموزش‌دیده، به‌ویژه MobileNet، برای تولید ویژگی‌های تصویری که می‌توانند در یادگیری انتقال استفاده شوند.
  • نحوه جمع‌آوری داده‌ها از وب‌کم کاربر برای چندین کلاس از داده‌هایی که می‌خواهید تشخیص دهید.
  • نحوه ایجاد و تعریف یک پرسپترون چندلایه که ویژگی های تصویر را می گیرد و می آموزد که اشیاء جدید را با استفاده از آنها طبقه بندی کند.

بیا هک کنیم...

آنچه شما نیاز دارید

  • یک حساب کاربری Glitch.com ترجیح داده می‌شود که همراه با آن دنبال شود، یا می‌توانید از یک محیط سرویس‌دهی وب استفاده کنید که خودتان راحت ویرایش و اجرا می‌کنید.

2. TensorFlow.js چیست؟

54e81d02971f53e8.png

TensorFlow.js یک کتابخانه یادگیری ماشین منبع باز است که می تواند در هر جایی که جاوا اسکریپت می تواند اجرا شود. این بر اساس کتابخانه اصلی TensorFlow نوشته شده در پایتون است و هدف آن ایجاد دوباره این تجربه توسعه‌دهنده و مجموعه‌ای از APIها برای اکوسیستم جاوا اسکریپت است.

کجا میشه ازش استفاده کرد؟

با توجه به قابلیت حمل جاوا اسکریپت، اکنون می توانید به 1 زبان بنویسید و یادگیری ماشینی را در تمامی پلتفرم های زیر به راحتی انجام دهید:

  • سمت کلاینت در مرورگر وب با استفاده از وانیلی جاوا اسکریپت
  • سمت سرور و حتی دستگاه های اینترنت اشیا مانند Raspberry Pi با استفاده از Node.js
  • برنامه های دسکتاپ با استفاده از Electron
  • برنامه های موبایل بومی با استفاده از React Native

TensorFlow.js همچنین از چندین پشتیبان در هر یک از این محیط‌ها پشتیبانی می‌کند (محیط‌های مبتنی بر سخت‌افزار واقعی که می‌تواند در داخل آن مانند CPU یا WebGL اجرا کند. یک "بک‌اند" در این زمینه به معنای یک محیط سمت سرور نیست - پشتیبان برای اجرا. به عنوان مثال می تواند سمت مشتری در WebGL باشد) تا از سازگاری اطمینان حاصل کند و همچنین کارها را سریع نگه دارد. در حال حاضر TensorFlow.js پشتیبانی می کند:

  • اجرای WebGL روی کارت گرافیک دستگاه (GPU) - این سریعترین راه برای اجرای مدلهای بزرگتر (با اندازه بیش از 3 مگابایت) با شتاب GPU است.
  • اجرای Web Assembly (WASM) بر روی CPU - برای بهبود عملکرد CPU در سراسر دستگاه ها از جمله تلفن های همراه نسل قدیمی تر. این برای مدل‌های کوچک‌تر (با حجم کمتر از 3 مگابایت) که در واقع می‌توانند در CPU با WASM سریع‌تر از WebGL اجرا شوند، مناسب‌تر است، زیرا بارگذاری محتوا در یک پردازنده گرافیکی زیاد است.
  • اجرای CPU - نسخه بازگشتی نباید هیچ یک از محیط های دیگر در دسترس باشد. این کندترین از این سه است اما همیشه برای شما آماده است.

توجه: اگر می‌دانید در چه دستگاهی اجرا می‌کنید، می‌توانید یکی از این پشتیبان‌ها را مجبور کنید، یا اگر این مورد را مشخص نکرده‌اید، به سادگی می‌توانید به TensorFlow.js اجازه دهید برای شما تصمیم بگیرد.

قدرت های فوق العاده سمت مشتری

اجرای TensorFlow.js در مرورگر وب روی دستگاه مشتری می تواند به چندین مزیت منجر شود که ارزش در نظر گرفتن دارد.

حریم خصوصی

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

سرعت

از آنجایی که مجبور نیستید داده ها را به یک سرور راه دور ارسال کنید، استنتاج (عمل طبقه بندی داده ها) می تواند سریعتر باشد. حتی بهتر از آن، دسترسی مستقیم به حسگرهای دستگاه مانند دوربین، میکروفون، GPS، شتاب سنج و موارد دیگر در صورتی که کاربر به شما اجازه دسترسی بدهد، دارید.

رسیدن و مقیاس

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

هزینه

بدون سرور به این معنی است که تنها چیزی که باید برای آن هزینه کنید یک CDN برای میزبانی فایل های HTML، CSS، JS و مدل شماست. هزینه CDN بسیار ارزان تر از نگه داشتن سرور (به طور بالقوه با کارت گرافیک متصل) در حال اجرا 24/7 است.

ویژگی های سمت سرور

استفاده از اجرای Node.js از TensorFlow.js ویژگی های زیر را فعال می کند.

پشتیبانی کامل از CUDA

در سمت سرور، برای شتاب کارت گرافیک، باید درایورهای NVIDIA CUDA را نصب کنید تا TensorFlow با کارت گرافیک کار کند (برخلاف مرورگری که از WebGL استفاده می کند - نیازی به نصب نیست). با این حال، با پشتیبانی کامل از CUDA، می‌توانید به طور کامل از توانایی‌های سطح پایین‌تر کارت گرافیک استفاده کنید، که منجر به آموزش سریع‌تر و زمان‌های استنتاج می‌شود. عملکرد با پیاده‌سازی Python TensorFlow برابری می‌کند، زیرا هر دوی آن‌ها از یک باطن ++C به اشتراک می‌گذارند.

سایز مدل

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

IOT

Node.js در رایانه های تک بردی محبوب مانند Raspberry Pi پشتیبانی می شود، که به نوبه خود به این معنی است که می توانید مدل های TensorFlow.js را در چنین دستگاه هایی نیز اجرا کنید.

سرعت

Node.js با جاوا اسکریپت نوشته شده است که به این معنی است که از کامپایل به موقع سود می برد. این بدان معنی است که شما اغلب هنگام استفاده از Node.js شاهد افزایش عملکرد خواهید بود، زیرا در زمان اجرا بهینه می شود، به خصوص برای هر پیش پردازشی که ممکن است انجام دهید. یک مثال عالی از این را می توان در این مطالعه موردی مشاهده کرد که نشان می دهد چگونه Hugging Face از Node.js برای افزایش عملکرد 2 برابری برای مدل پردازش زبان طبیعی خود استفاده کرد.

اکنون اصول اولیه TensorFlow.js، جایی که می‌تواند اجرا شود و برخی از مزایای آن را می‌دانید، بیایید شروع کنیم به انجام کارهای مفید با آن!

3. انتقال یادگیری

یادگیری انتقالی دقیقا چیست؟

یادگیری انتقالی شامل گرفتن دانشی است که قبلاً آموخته شده است تا به یادگیری یک چیز متفاوت اما مشابه کمک کند.

ما انسان ها همیشه این کار را انجام می دهیم. شما یک عمر تجربیات در مغز خود دارید که می توانید از آنها برای تشخیص چیزهای جدیدی که قبلاً ندیده اید استفاده کنید. به عنوان مثال این درخت بید را در نظر بگیرید:

e28070392cd4afb9.png

بسته به اینکه در کجای دنیا هستید، این احتمال وجود دارد که قبلاً این نوع درخت را ندیده باشید.

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

d9073a0d5df27222.png

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

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

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

در این انیمیشن می توانید تعداد بسیار زیادی از لایه های موجود در این مدل MobileNet V1 را مشاهده کنید:

7d4e1e35c1a89715.gif

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

بیایید نگاهی به معماری سنتی شبکه عصبی کانولوشنال (CNN) بیندازیم (شبیه به MobileNet) و ببینیم که چگونه یادگیری انتقال می تواند این شبکه آموزش دیده را برای یادگیری چیزهای جدید اهرمی کند. تصویر زیر معماری مدل معمولی یک CNN را نشان می دهد که در این مورد برای تشخیص ارقام دست نویس از 0 تا 9 آموزش دیده است:

baf4e3d434576106.png

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

369a8a9041c6917d.png

با فرض اینکه چیز جدیدی که می‌خواهید تشخیص دهید می‌تواند از چنین ویژگی‌های خروجی که مدل قبلی آموخته است نیز استفاده کند، در این صورت شانس خوبی وجود دارد که بتوان از آنها برای یک هدف جدید استفاده مجدد کرد.

در نمودار بالا، این مدل فرضی بر روی ارقام آموزش داده شده است، بنابراین شاید آنچه در مورد ارقام آموخته شده است را بتوان برای حروفی مانند a، b و c نیز اعمال کرد.

بنابراین اکنون می توانید یک سر طبقه بندی جدید اضافه کنید که سعی می کند a، b یا c را پیش بینی کند، همانطور که نشان داده شده است:

db97e5e60ae73bbd.png

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

عمل انجام این کار به عنوان یادگیری انتقالی شناخته می شود و همان کاری است که Teachable Machine در پشت صحنه انجام می دهد.

همچنین می توانید ببینید که تنها با آموزش پرسپترون چند لایه در انتهای شبکه، بسیار سریعتر از زمانی که مجبور باشید کل شبکه را از ابتدا آموزش دهید، تمرین می کند.

اما چگونه می توانید به بخش های فرعی یک مدل دست پیدا کنید؟ برای اطلاع از این موضوع به بخش بعدی بروید.

4. TensorFlow Hub - مدل های پایه

یک مدل پایه مناسب برای استفاده پیدا کنید

برای مدل‌های تحقیقاتی پیشرفته‌تر و محبوب‌تر مانند MobileNet، می‌توانید به TensorFlow hub بروید و سپس مدل‌های مناسب برای TensorFlow.js که از معماری MobileNet v3 استفاده می‌کنند را فیلتر کنید تا نتایجی مانند آنچه در اینجا نشان داده شده است را بیابید:

c5dc1420c6238c14.png

توجه داشته باشید که برخی از این نتایج از نوع "طبقه بندی تصویر" هستند (مفصل در سمت چپ بالای هر نتیجه کارت مدل)، و برخی دیگر از نوع "بردار ویژگی تصویر" هستند.

این نتایج Image Feature Vector اساساً نسخه‌های از پیش خرد شده MobileNet هستند که می‌توانید از آن‌ها برای دریافت بردارهای ویژگی تصویر به جای طبقه‌بندی نهایی استفاده کنید.

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

مورد بعدی که باید بررسی شود این است که برای یک مدل پایه مورد علاقه، مدل با چه فرمتی TensorFlow.js منتشر شده است. اگر صفحه یکی از این مدل‌های بردار ویژگی MobileNet v3 را باز کنید، می‌توانید از مستندات JS ببینید که به شکل یک مدل نمودار بر اساس قطعه کد مثال در مستندات است که از tf.loadGraphModel() استفاده می‌کند.

f97d903d2e46924b.png

همچنین لازم به ذکر است که اگر مدلی را در قالب لایه ها به جای قالب نمودار پیدا کردید، می توانید انتخاب کنید که کدام لایه ها را فریز کنید و کدام را برای آموزش از حالت انجماد خارج کنید. این می تواند هنگام ایجاد یک مدل برای یک کار جدید، که اغلب به عنوان "مدل انتقال" نامیده می شود، بسیار قدرتمند باشد. با این حال، در حال حاضر، شما از نوع مدل نمودار پیش فرض برای این آموزش استفاده خواهید کرد، که اکثر مدل های TF Hub به عنوان آن استفاده می شوند. برای کسب اطلاعات بیشتر در مورد کار با مدل‌های لایه‌ها، دوره Zero to Hero TensorFlow.js را بررسی کنید.

مزایای یادگیری انتقالی

مزایای استفاده از آموزش انتقال به جای آموزش کل معماری مدل از ابتدا چیست؟

اولاً، زمان آموزش یک مزیت کلیدی برای استفاده از رویکرد یادگیری انتقالی است، زیرا شما قبلاً یک مدل پایه آموزش دیده برای ساخت دارید.

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

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

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

بسيار خوب! اکنون شما ماهیت آموزش انتقالی را می‌دانید، زمان آن رسیده است که نسخه خود را از Teachable Machine ایجاد کنید. بیا شروع کنیم!

5. برای کدگذاری تنظیم شوید

آنچه شما نیاز دارید

  • یک مرورگر وب مدرن
  • دانش اولیه HTML، CSS، JavaScript و Chrome DevTools (مشاهده خروجی کنسول).

بیایید کدنویسی کنیم

قالب‌های Boilerplate برای شروع برای Glitch.com یا Codepen.io ایجاد شده‌اند. شما به سادگی می توانید هر یک از الگوها را به عنوان حالت پایه خود برای این آزمایشگاه کد، تنها با یک کلیک کلون کنید.

در Glitch، روی دکمه " remix this" کلیک کنید تا آن را فورک کنید و مجموعه جدیدی از فایل ها را ایجاد کنید که می توانید ویرایش کنید.

از طرف دیگر، در Codepen، روی " fork" در پایین سمت راست پایین صفحه کلیک کنید.

این اسکلت بسیار ساده فایل های زیر را در اختیار شما قرار می دهد:

  • صفحه HTML (index.html)
  • صفحه سبک (style.css)
  • فایل برای نوشتن کد جاوا اسکریپت ما (script.js)

برای راحتی شما، یک import اضافه شده در فایل HTML برای کتابخانه TensorFlow.js وجود دارد. به نظر می رسد این است:

index.html

<!-- Import TensorFlow.js library -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js" type="text/javascript"></script>

جایگزین: از وب سایت دلخواه خود استفاده کنید یا به صورت محلی کار کنید

اگر می‌خواهید کد را دانلود کنید و به صورت محلی یا روی یک ویرایشگر آنلاین دیگر کار کنید، به سادگی 3 فایل نام‌گذاری شده در بالا را در همان دایرکتوری ایجاد کنید و کد را از Glitch boilerplate ما در هر یک از آنها کپی و جای‌گذاری کنید.

6. برنامه HTML boilerplate

از کجا آغاز کنم؟

همه نمونه‌های اولیه به داربست‌های اولیه HTML نیاز دارند که می‌توانید یافته‌های خود را روی آن‌ها ارائه دهید. اکنون آن را تنظیم کنید. قرار است اضافه کنید:

  • عنوانی برای صفحه
  • چند متن توصیفی
  • یک پاراگراف وضعیت
  • ویدیویی برای نگه داشتن فید وب‌کم پس از آماده شدن.
  • چندین دکمه برای راه اندازی دوربین، جمع آوری داده یا بازنشانی تجربه.
  • فایل‌های TensorFlow.js و JS را وارد می‌کند که بعداً کدنویسی می‌کنید.

index.html را باز کنید و کد موجود را با موارد زیر بچسبانید تا ویژگی های بالا را تنظیم کنید:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Transfer Learning - TensorFlow.js</title>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- Import the webpage's stylesheet -->
    <link rel="stylesheet" href="/style.css">
  </head>  
  <body>
    <h1>Make your own "Teachable Machine" using Transfer Learning with MobileNet v3 in TensorFlow.js using saved graph model from TFHub.</h1>
    
    <p id="status">Awaiting TF.js load</p>
    
    <video id="webcam" autoplay muted></video>
    
    <button id="enableCam">Enable Webcam</button>
    <button class="dataCollector" data-1hot="0" data-name="Class 1">Gather Class 1 Data</button>
    <button class="dataCollector" data-1hot="1" data-name="Class 2">Gather Class 2 Data</button>
    <button id="train">Train &amp; Predict!</button>
    <button id="reset">Reset</button>

    <!-- Import TensorFlow.js library -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@3.11.0/dist/tf.min.js" type="text/javascript"></script>

    <!-- Import the page's JavaScript to do some stuff -->
    <script type="module" src="/script.js"></script>
  </body>
</html>

ویران کن

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

  • شما یک تگ <h1> برای عنوان صفحه به همراه یک تگ <p> با شناسه "وضعیت" اضافه کردید، جایی که اطلاعات را در آن چاپ خواهید کرد، زیرا از قسمت های مختلف سیستم برای مشاهده خروجی ها استفاده می کنید.
  • شما یک عنصر <video> با شناسه "وب کم" اضافه کردید که بعداً جریان وب کم خود را به آن رندر خواهید داد.
  • شما 5 عنصر <button> را اضافه کردید. اولین مورد، با شناسه "enableCam" دوربین را فعال می کند. دو دکمه بعدی دارای یک کلاس از DataCollector هستند که به شما امکان می دهد تصاویر نمونه ای را برای اشیایی که می خواهید تشخیص دهید جمع آوری کنید. کدی که بعداً می نویسید طوری طراحی می شود که می توانید هر تعداد از این دکمه ها را اضافه کنید و به طور خودکار همانطور که در نظر گرفته شده است کار می کنند.

توجه داشته باشید که این دکمه ها همچنین دارای یک ویژگی خاص تعریف شده توسط کاربر به نام data-1hot هستند که یک مقدار صحیح از 0 برای کلاس اول شروع می شود. این شاخص عددی است که برای نمایش داده های یک کلاس خاص استفاده می کنید. این شاخص برای رمزگذاری صحیح کلاس های خروجی با نمایش عددی به جای رشته استفاده می شود، زیرا مدل های ML فقط می توانند با اعداد کار کنند.

همچنین یک ویژگی data-name وجود دارد که حاوی نام قابل خواندن برای انسان است که می‌خواهید برای این کلاس استفاده کنید، که به شما امکان می‌دهد به جای یک مقدار شاخص عددی از کدگذاری 1 داغ، نام معنادارتری برای کاربر ارائه دهید.

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

  • شما همچنین 2 واردات <script> اضافه کردید. یکی برای TensorFlow.js و دیگری برای script.js که به زودی تعریف خواهید کرد.

7. اضافه کردن سبک

پیش فرض عنصر

برای اطمینان از نمایش صحیح عناصر HTML که به تازگی اضافه کرده اید، سبک اضافه کنید. در اینجا چند سبک وجود دارد که به درستی به عناصر موقعیت و اندازه اضافه شده اند. هیچ چیز خیلی خاصی نیست. مطمئناً می توانید بعداً به این اضافه کنید تا یک UX حتی بهتر بسازید، همانطور که در ویدیوی ماشین قابل آموزش دیدید.

style.css

body {
  font-family: helvetica, arial, sans-serif;
  margin: 2em;
}

h1 {
  font-style: italic;
  color: #FF6F00;
}


video {
  clear: both;
  display: block;
  margin: 10px;
  background: #000000;
  width: 640px;
  height: 480px;
}

button {
  padding: 10px;
  float: left;
  margin: 5px 3px 5px 10px;
}

.removed {
  display: none;
}

#status {
  font-size:150%;
}

عالی! این تمام چیزی است که شما نیاز دارید. اگر در حال حاضر پیش نمایش خروجی را مشاهده کنید، باید چیزی شبیه به این باشد:

81909685d7566dcb.png

8. جاوا اسکریپت: ثابت های کلیدی و شنوندگان

ثابت های کلیدی را تعریف کنید

ابتدا چند ثابت کلیدی را که در سراسر برنامه استفاده خواهید کرد، اضافه کنید. با جایگزین کردن محتویات script.js با این ثابت ها شروع کنید:

script.js

const STATUS = document.getElementById('status');
const VIDEO = document.getElementById('webcam');
const ENABLE_CAM_BUTTON = document.getElementById('enableCam');
const RESET_BUTTON = document.getElementById('reset');
const TRAIN_BUTTON = document.getElementById('train');
const MOBILE_NET_INPUT_WIDTH = 224;
const MOBILE_NET_INPUT_HEIGHT = 224;
const STOP_DATA_GATHER = -1;
const CLASS_NAMES = [];

بیایید تجزیه کنیم که اینها برای چه هستند:

  • STATUS به سادگی یک ارجاع به تگ پاراگراف دارد که به روز رسانی وضعیت را روی آن می نویسید.
  • VIDEO یک ارجاع به عنصر ویدیوی HTML دارد که فید وب‌کم را ارائه می‌کند.
  • ENABLE_CAM_BUTTON ، RESET_BUTTON ، و TRAIN_BUTTON ارجاعات DOM را به تمام دکمه های کلیدی صفحه HTML می گیرند.
  • MOBILE_NET_INPUT_WIDTH و MOBILE_NET_INPUT_HEIGHT به ترتیب عرض و ارتفاع ورودی مورد انتظار مدل MobileNet را تعریف می کنند. با ذخیره کردن آن در یک ثابت نزدیک بالای فایل مانند این، اگر بعداً تصمیم به استفاده از نسخه دیگری دارید، به‌روزرسانی مقادیر به‌جای تعویض آن در مکان‌های مختلف آسان‌تر می‌شود.
  • STOP_DATA_GATHER روی - 1 تنظیم شده است. این یک مقدار حالت را ذخیره می کند تا بدانید کاربر چه زمانی از کلیک روی دکمه برای جمع آوری داده ها از فید وب کم خودداری کرده است. با دادن نام معنادارتر به این عدد، کد را بعدا خواناتر می کند.
  • CLASS_NAMES به عنوان جستجوگر عمل می کند و نام های قابل خواندن انسان را برای پیش بینی های کلاس ممکن نگه می دارد. این آرایه بعداً پر می شود.

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

شنوندگان رویدادهای کلیدی را اضافه کنید

همانطور که نشان داده شده است، با افزودن کنترل کننده رویداد کلیک به دکمه های کلید شروع کنید:

script.js

ENABLE_CAM_BUTTON.addEventListener('click', enableCam);
TRAIN_BUTTON.addEventListener('click', trainAndPredict);
RESET_BUTTON.addEventListener('click', reset);


function enableCam() {
  // TODO: Fill this out later in the codelab!
}


function trainAndPredict() {
  // TODO: Fill this out later in the codelab!
}


function reset() {
  // TODO: Fill this out later in the codelab!
}

ENABLE_CAM_BUTTON - با کلیک کردن، تابع enableCam را فراخوانی می کند.

TRAIN_BUTTON - با کلیک روی trainAndPredict تماس می گیرد.

RESET_BUTTON - با کلیک کردن، تماس ها بازنشانی می شوند.

در نهایت در این بخش می‌توانید همه دکمه‌هایی را که دارای یک کلاس از 'dataCollector' هستند با استفاده از document.querySelectorAll() بیابید. این آرایه ای از عناصر پیدا شده از سند را برمی گرداند که مطابقت دارند:

script.js

let dataCollectorButtons = document.querySelectorAll('button.dataCollector');
for (let i = 0; i < dataCollectorButtons.length; i++) {
  dataCollectorButtons[i].addEventListener('mousedown', gatherDataForClass);
  dataCollectorButtons[i].addEventListener('mouseup', gatherDataForClass);
  // Populate the human readable names for classes.
  CLASS_NAMES.push(dataCollectorButtons[i].getAttribute('data-name'));
}


function gatherDataForClass() {
  // TODO: Fill this out later in the codelab!
}

توضیح کد:

سپس از طریق دکمه‌های پیدا شده تکرار می‌کنید و 2 شنونده رویداد را به هر کدام مرتبط می‌کنید. یکی برای "Download" و دیگری برای "Mouseup". این به شما امکان می دهد تا زمانی که دکمه فشرده است، نمونه ها را ضبط کنید، که برای جمع آوری داده ها مفید است.

هر دو رویداد یک تابع gatherDataForClass را فراخوانی می کنند که بعداً تعریف خواهید کرد.

در این مرحله، می‌توانید نام‌های کلاس‌های قابل خواندن انسان پیدا شده را از ویژگی دکمه HTML data-name به آرایه CLASS_NAMES فشار دهید.

در مرحله بعد، چند متغیر برای ذخیره موارد کلیدی که بعداً استفاده خواهند شد، اضافه کنید.

script.js

let mobilenet = undefined;
let gatherDataState = STOP_DATA_GATHER;
let videoPlaying = false;
let trainingDataInputs = [];
let trainingDataOutputs = [];
let examplesCount = [];
let predict = false;

بیایید از میان آنها عبور کنیم.

ابتدا، شما یک mobilenet متغیر برای ذخیره مدل mobilenet بارگذاری شده دارید. ابتدا این را روی undefined تنظیم کنید.

بعد، شما یک متغیر به نام gatherDataState دارید. اگر یک دکمه 'dataCollector' فشار داده شود، به جای آن، همانطور که در HTML تعریف شده است، به عنوان 1 شناسه داغ آن دکمه تغییر می کند، بنابراین شما می دانید که در آن لحظه چه دسته ای از داده ها را جمع آوری می کنید. در ابتدا، این روی STOP_DATA_GATHER تنظیم می‌شود تا حلقه جمع‌آوری داده‌ای که بعداً می‌نویسید، وقتی هیچ دکمه‌ای فشار داده نمی‌شود، داده‌ای را جمع‌آوری نکند.

videoPlaying پیگیری می کند که آیا جریان وب کم با موفقیت بارگیری و پخش شده است و برای استفاده در دسترس است یا خیر. در ابتدا، این روی false تنظیم می‌شود، زیرا وب‌کم روشن نیست تا زمانی که ENABLE_CAM_BUTTON.

بعد، 2 آرایه، trainingDataInputs و trainingDataOutputs تعریف کنید. این مقادیر داده‌های آموزشی جمع‌آوری‌شده را ذخیره می‌کنند، همانطور که روی دکمه‌های 'dataCollector' برای ویژگی‌های ورودی تولید شده توسط مدل پایه MobileNet و کلاس خروجی نمونه‌گیری شده به ترتیب کلیک می‌کنید.

یک آرایه نهایی، examplesCount, برای پیگیری تعداد مثال‌های موجود برای هر کلاس پس از شروع اضافه کردن آنها تعریف می‌شود.

در نهایت، شما متغیری به نام predict دارید که حلقه پیش بینی شما را کنترل می کند. این در ابتدا روی false تنظیم شده است. هیچ پیش‌بینی نمی‌تواند انجام شود تا زمانی که بعداً به true تنظیم شود.

اکنون که همه متغیرهای کلیدی تعریف شده اند، بیایید برویم و مدل پایه MobileNet v3 را که از قبل خرد شده است بارگذاری کنیم که به جای طبقه بندی، بردارهای ویژگی تصویر را ارائه می دهد.

9. مدل پایه MobileNet را بارگذاری کنید

ابتدا تابع جدیدی به نام loadMobileNetFeatureModel را مطابق شکل زیر تعریف کنید. این باید یک تابع ناهمگام باشد زیرا عمل بارگیری یک مدل ناهمزمان است:

script.js

/**
 * Loads the MobileNet model and warms it up so ready for use.
 **/
async function loadMobileNetFeatureModel() {
  const URL = 
    'https://tfhub.dev/google/tfjs-model/imagenet/mobilenet_v3_small_100_224/feature_vector/5/default/1';
  
  mobilenet = await tf.loadGraphModel(URL, {fromTFHub: true});
  STATUS.innerText = 'MobileNet v3 loaded successfully!';
  
  // Warm up the model by passing zeros through it once.
  tf.tidy(function () {
    let answer = mobilenet.predict(tf.zeros([1, MOBILE_NET_INPUT_HEIGHT, MOBILE_NET_INPUT_WIDTH, 3]));
    console.log(answer.shape);
  });
}

// Call the function immediately to start loading.
loadMobileNetFeatureModel();

در این کد شما URL که در آن مدل بارگیری می شود از مستندات TFHub تعریف می کنید.

سپس می‌توانید مدل را با استفاده از await tf.loadGraphModel() بارگیری کنید، و به یاد داشته باشید که هنگام بارگیری یک مدل از این وب‌سایت Google، ویژگی خاص fromTFHub را روی true تنظیم کنید. این یک مورد خاص فقط برای استفاده از مدل‌های میزبانی شده در TF Hub است که در آن ویژگی اضافی باید تنظیم شود.

هنگامی که بارگیری کامل شد، می توانید innerText عنصر STATUS را با یک پیام تنظیم کنید تا بتوانید به صورت بصری مشاهده کنید که به درستی بارگیری شده است و آماده شروع جمع آوری داده ها هستید.

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

می توانید از tf.zeros() پیچیده شده در tf.tidy() استفاده کنید تا مطمئن شوید که تانسورها به درستی دور ریخته می شوند، با اندازه دسته ای 1، و ارتفاع و عرض صحیحی که در ابتدا در ثابت های خود تعریف کرده اید. در نهایت، کانال های رنگی را نیز مشخص می کنید که در این حالت 3 است زیرا مدل انتظار تصاویر RGB را دارد.

سپس، شکل حاصل از تانسور را با استفاده از answer.shape() ثبت کنید تا به شما در درک اندازه ویژگی های تصویری که این مدل تولید می کند کمک کند.

پس از تعریف این تابع، می توانید بلافاصله آن را فراخوانی کنید تا دانلود مدل در بارگذاری صفحه آغاز شود.

اگر همین الان پیش‌نمایش زنده خود را مشاهده کنید، پس از چند لحظه می‌بینید که متن وضعیت از "در انتظار بارگیری TF.js" به "MobileNet v3 با موفقیت بارگیری شد!" همانطور که در زیر نشان داده شده است. قبل از ادامه، مطمئن شوید که این کار می کند.

a28b734e190afff.png

همچنین می توانید خروجی کنسول را بررسی کنید تا اندازه چاپ شده ویژگی های خروجی را که این مدل تولید می کند، ببینید. پس از اجرای صفرها از طریق مدل MobileNet، شکلی از [1, 1024] مشاهده خواهید کرد. اولین مورد فقط به اندازه یک دسته است و می‌توانید ببینید که در واقع 1024 ویژگی را برمی‌گرداند که می‌توان از آنها برای طبقه‌بندی اشیاء جدید استفاده کرد.

10. سر مدل جدید را تعریف کنید

اکنون زمان آن است که مدل سر خود را تعریف کنید، که اساساً یک پرسپترون چند لایه بسیار کم است.

script.js

let model = tf.sequential();
model.add(tf.layers.dense({inputShape: [1024], units: 128, activation: 'relu'}));
model.add(tf.layers.dense({units: CLASS_NAMES.length, activation: 'softmax'}));

model.summary();

// Compile the model with the defined optimizer and specify a loss function to use.
model.compile({
  // Adam changes the learning rate over time which is useful.
  optimizer: 'adam',
  // Use the correct loss function. If 2 classes of data, must use binaryCrossentropy.
  // Else categoricalCrossentropy is used if more than 2 classes.
  loss: (CLASS_NAMES.length === 2) ? 'binaryCrossentropy': 'categoricalCrossentropy', 
  // As this is a classification problem you can record accuracy in the logs too!
  metrics: ['accuracy']  
});

بیایید این کد را مرور کنیم. شما با تعریف یک مدل tf.sequential شروع می کنید که لایه های مدل را به آن اضافه می کنید.

سپس یک لایه متراکم به عنوان لایه ورودی به این مدل اضافه کنید. این دارای شکل ورودی 1024 است زیرا خروجی های ویژگی های MobileNet v3 به این اندازه هستند. این را در مرحله قبل پس از عبور از مدل کشف کردید. این لایه دارای 128 نورون است که از تابع فعال سازی ReLU استفاده می کنند.

اگر با توابع فعال‌سازی و لایه‌های مدل تازه کار هستید، دوره‌ای را که در ابتدای این کارگاه توضیح داده شده است را در نظر بگیرید تا بفهمید این ویژگی‌ها در پشت صحنه چه می‌کنند.

لایه بعدی که اضافه می شود لایه خروجی است. تعداد نورون‌ها باید برابر با تعداد کلاس‌هایی باشد که می‌خواهید پیش‌بینی کنید. برای انجام این کار، می‌توانید از CLASS_NAMES.length برای پیدا کردن تعداد کلاس‌هایی که می‌خواهید طبقه‌بندی کنید، استفاده کنید، که برابر با تعداد دکمه‌های جمع‌آوری داده‌های موجود در رابط کاربری است. از آنجایی که این یک مشکل طبقه بندی است، شما از فعال سازی softmax در این لایه خروجی استفاده می کنید، که باید هنگام ایجاد مدلی برای حل مسائل طبقه بندی به جای رگرسیون استفاده شود.

اکنون یک model.summary() چاپ کنید تا نمای کلی مدل جدید تعریف شده را در کنسول چاپ کنید.

در نهایت مدل را کامپایل کنید تا برای آموزش آماده شود. در اینجا بهینه‌ساز روی adam تنظیم می‌شود، و اگر CLASS_NAMES.length برابر با 2 باشد، ضرر یا binaryCrossentropy خواهد بود، یا اگر 3 کلاس یا بیشتر برای طبقه‌بندی وجود داشته باشد، از categoricalCrossentropy استفاده می‌کند. معیارهای دقت نیز درخواست می‌شود تا بتوان بعداً برای اهداف اشکال‌زدایی در گزارش‌ها نظارت کرد.

در کنسول شما باید چیزی شبیه به این را ببینید:

22eaf32286fea4bb.png

توجه داشته باشید که این دارای بیش از 130 هزار پارامتر قابل آموزش است. اما از آنجایی که این یک لایه متراکم ساده از نورون های معمولی است، بسیار سریع تمرین می کند.

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

11. وب کم را فعال کنید

اکنون زمان آن رسیده است که تابع enableCam() را که قبلاً تعریف کرده بودید تکمیل کنید. یک تابع جدید به نام hasGetUserMedia() مطابق شکل زیر اضافه کنید و سپس محتویات تابع enableCam() تعریف شده قبلی را با کد مربوطه در زیر جایگزین کنید.

script.js

function hasGetUserMedia() {
  return !!(navigator.mediaDevices && navigator.mediaDevices.getUserMedia);
}

function enableCam() {
  if (hasGetUserMedia()) {
    // getUsermedia parameters.
    const constraints = {
      video: true,
      width: 640, 
      height: 480 
    };

    // Activate the webcam stream.
    navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
      VIDEO.srcObject = stream;
      VIDEO.addEventListener('loadeddata', function() {
        videoPlaying = true;
        ENABLE_CAM_BUTTON.classList.add('removed');
      });
    });
  } else {
    console.warn('getUserMedia() is not supported by your browser');
  }
}

ابتدا، یک تابع به نام hasGetUserMedia() ایجاد کنید تا با بررسی وجود ویژگی های کلیدی API مرورگر، بررسی کنید که آیا مرورگر از getUserMedia() پشتیبانی می کند یا خیر.

در enableCam() enableCam از تابع hasGetUserMedia() که در بالا تعریف کردید استفاده کنید تا بررسی کنید که آیا پشتیبانی می شود یا خیر. اگر اینطور نیست، یک هشدار در کنسول چاپ کنید.

اگر از آن پشتیبانی می‌کند، محدودیت‌هایی را برای تماس getUserMedia() خود تعریف کنید، مانند اینکه شما فقط پخش ویدیو را می‌خواهید، و ترجیح می‌دهید width ویدیو 640 پیکسل و height 480 پیکسل باشد. چرا؟ خوب، دریافت ویدیویی بزرگتر از این فایده ای ندارد زیرا برای وارد شدن به مدل MobileNet باید اندازه آن به 224 در 224 پیکسل تغییر یابد. همچنین می توانید با درخواست رزولوشن کمتر، برخی منابع محاسباتی را ذخیره کنید. اکثر دوربین ها از وضوحی با این اندازه پشتیبانی می کنند.

سپس، navigator.mediaDevices.getUserMedia() را با constraints که در بالا توضیح داده شد، فراخوانی کنید و سپس منتظر بمانید تا stream برگردانده شود. پس از بازگشت stream ، می توانید عنصر VIDEO خود را با تنظیم آن به عنوان مقدار srcObject ، برای پخش stream دریافت کنید.

همچنین باید یک eventListener روی عنصر VIDEO اضافه کنید تا بدانید چه زمانی stream بارگیری شده و با موفقیت در حال پخش است.

پس از بارگیری استیم، می‌توانید videoPlaying روی true تنظیم کنید و ENABLE_CAM_BUTTON را حذف کنید تا با تنظیم کلاس آن بر روی " removed " از کلیک مجدد روی آن جلوگیری کنید.

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

b378eb1affa9b883.png

خوب، اکنون زمان اضافه کردن یک تابع برای مقابله با کلیک دکمه dataCollector است.

12. کنترل کننده رویداد دکمه جمع آوری داده ها

اکنون زمان آن رسیده است که تابع خالی فعلی خود را به نام gatherDataForClass(). این همان چیزی است که شما به عنوان عملکرد کنترل کننده رویداد خود برای دکمه های dataCollector در شروع Codelab اختصاص داده اید.

script.js

/**
 * Handle Data Gather for button mouseup/mousedown.
 **/
function gatherDataForClass() {
  let classNumber = parseInt(this.getAttribute('data-1hot'));
  gatherDataState = (gatherDataState === STOP_DATA_GATHER) ? classNumber : STOP_DATA_GATHER;
  dataGatherLoop();
}

ابتدا با فراخوانی this.getAttribute() با نام ویژگی، در این مورد data-1hot data-1hot به عنوان پارامتر، ویژگی data-1hot را روی دکمه ای که در حال حاضر کلیک شده است، بررسی کنید. از آنجایی که این یک رشته است، می‌توانید از parseInt() برای فرستادن آن به یک عدد صحیح استفاده کنید و این نتیجه را به متغیری به نام classNumber.

سپس، متغیر gatherDataState را بر اساس آن تنظیم کنید. اگر gatherDataState فعلی برابر با STOP_DATA_GATHER باشد (که شما آن را -1 تنظیم کرده‌اید)، به این معنی است که شما در حال حاضر هیچ داده‌ای را جمع‌آوری نمی‌کنید و این یک رویداد mousedown بود که فعال شد. gatherDataState به عنوان classNumber که تازه پیدا کردید تنظیم کنید.

در غیر این صورت، به این معنی است که شما در حال حاضر در حال جمع‌آوری داده‌ها هستید و رویدادی که اجرا می‌شود یک رویداد mouseup بود، و اکنون می‌خواهید جمع‌آوری داده برای آن کلاس را متوقف کنید. فقط کافی است آن را به حالت STOP_DATA_GATHER تنظیم کنید تا حلقه جمع آوری داده ها به زودی تعریف کنید.

سرانجام ، تماس با dataGatherLoop(), که در واقع ضبط داده های کلاس را انجام می دهد.

13. جمع آوری داده ها

اکنون عملکرد dataGatherLoop() را تعریف کنید. این عملکرد وظیفه نمونه برداری از تصاویر از فیلم وب کم ، عبور از آنها از طریق مدل Mobilenet و گرفتن خروجی های آن مدل (بردارهای ویژگی 1024) است.

سپس آنها را به همراه شناسه gatherDataState از دکمه ای که در حال حاضر تحت فشار قرار می گیرد ، ذخیره می کند ، بنابراین می دانید این داده ها چه کلاس را نشان می دهد.

بیایید از طریق آن قدم بزنیم:

script.js

function dataGatherLoop() {
  if (videoPlaying && gatherDataState !== STOP_DATA_GATHER) {
    let imageFeatures = tf.tidy(function() {
      let videoFrameAsTensor = tf.browser.fromPixels(VIDEO);
      let resizedTensorFrame = tf.image.resizeBilinear(videoFrameAsTensor, [MOBILE_NET_INPUT_HEIGHT, 
          MOBILE_NET_INPUT_WIDTH], true);
      let normalizedTensorFrame = resizedTensorFrame.div(255);
      return mobilenet.predict(normalizedTensorFrame.expandDims()).squeeze();
    });

    trainingDataInputs.push(imageFeatures);
    trainingDataOutputs.push(gatherDataState);
    
    // Intialize array index element if currently undefined.
    if (examplesCount[gatherDataState] === undefined) {
      examplesCount[gatherDataState] = 0;
    }
    examplesCount[gatherDataState]++;

    STATUS.innerText = '';
    for (let n = 0; n < CLASS_NAMES.length; n++) {
      STATUS.innerText += CLASS_NAMES[n] + ' data count: ' + examplesCount[n] + '. ';
    }
    window.requestAnimationFrame(dataGatherLoop);
  }
}

شما فقط قصد دارید اجرای این عملکرد را ادامه دهید اگر videoPlaying صحیح باشد ، به این معنی که وب کم فعال است ، و gatherDataState با STOP_DATA_GATHER برابر نیست و در حال حاضر یک دکمه برای جمع آوری داده های کلاس فشار می یابد.

در مرحله بعد ، کد خود را در یک tf.tidy() بپیچید تا هرگونه تانسور ایجاد شده را در کد زیر دفع کنید. نتیجه این اجرای کد tf.tidy() در متغیری به نام imageFeatures ذخیره می شود.

اکنون می توانید با استفاده از tf.browser.fromPixels() یک قاب از VIDEO وب کم بگیرید. تانسور حاصل حاوی داده های تصویر در متغیری به نام videoFrameAsTensor ذخیره می شود.

در مرحله بعد ، متغیر videoFrameAsTensor را تغییر دهید تا از شکل صحیح برای ورودی مدل Mobilenet استفاده شود. از یک تماس tf.image.resizeBilinear() با تانسور که می خواهید به عنوان اولین پارامتر تغییر شکل دهید استفاده کنید ، و سپس شکلی که ارتفاع و عرض جدید را مطابق ثابت هایی که قبلاً ایجاد کرده اید تعریف می کند. سرانجام ، با عبور از پارامتر سوم ، گوشه های تراز را تنظیم کنید تا از هرگونه مشکل تراز در هنگام تغییر اندازه جلوگیری کنید. نتیجه این تغییر اندازه در متغیری به نام resizedTensorFrame ذخیره می شود.

توجه داشته باشید که این تغییر اندازه بدوی تصویر را دراز می کند ، زیرا تصویر وب کم شما 640 در 480 پیکسل است و مدل به یک تصویر مربع از 224 در 224 پیکسل نیاز دارد.

برای اهداف این نسخه ی نمایشی باید خوب کار کند. با این حال ، پس از اتمام این CodeLab ، ممکن است بخواهید به جای آن حتی برای نتایج بهتر برای هر سیستم تولیدی که بعداً ایجاد می کنید ، یک مربع را از این تصویر بردارید.

بعد ، داده های تصویر را عادی کنید. داده های تصویر همیشه هنگام استفاده از tf.browser.frompixels() در محدوده 0 تا 255 است ، بنابراین می توانید به سادگی اندازه گیری ResizedTensorFrame را 255 تقسیم کنید تا اطمینان حاصل شود که تمام مقادیر بین 0 تا 1 هستند ، این همان چیزی است که مدل Mobilenet به عنوان ورودی انتظار دارد.

سرانجام ، در بخش tf.tidy() کد ، این تانسور نرمال شده را از طریق مدل بارگذاری شده با فراخوانی mobilenet.predict() فشار دهید ، که نسخه گسترده ای از normalizedTensorFrame را با استفاده از expandDims() منتقل می کنید تا یک دسته باشد از 1 ، به عنوان مدل انتظار می رود دسته ای از ورودی ها برای پردازش باشد.

پس از بازگشت نتیجه ، می توانید بلافاصله تماس بگیرید squeeze() در نتیجه برگشتی ، آن را به پایین تانسور 1D بکشید ، که سپس برمی گردید و به متغیر imageFeatures اختصاص می دهید که نتیجه را از tf.tidy() ضبط می کند.

اکنون که از مدل Mobilenet imageFeatures را دارید ، می توانید با فشار دادن آنها به آرایه trainingDataInputs که قبلاً تعریف کرده اید ، آنها را ضبط کنید.

همچنین می توانید با فشار دادن gatherDataState فعلی به آرایه trainingDataOutputs ، این ورودی را ثبت کنید.

توجه داشته باشید که متغیر gatherDataState می تواند بر روی شناسه عددی کلاس فعلی که شما در حال ضبط داده ها در هنگام کلیک بر روی دکمه در عملکرد قبلاً تعریف شده gatherDataForClass() تنظیم شده است.

در این مرحله همچنین می توانید تعداد مثالهایی را که برای یک کلاس خاص دارید افزایش دهید. برای انجام این کار ، ابتدا بررسی کنید که آیا شاخص موجود در آرایه examplesCount قبل از آن آغاز شده است یا خیر. اگر تعریف نشده است ، آن را روی 0 تنظیم کنید تا پیشخوان را برای شناسه عددی یک کلاس خاص تنظیم کنید ، و سپس می توانید examplesCount برای gatherDataState فعلی افزایش دهید.

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

در آخر ، با window.requestAnimationFrame() با dataGatherLoop به عنوان یک پارامتر عبور کنید تا دوباره به صورت بازگشتی این عملکرد را فراخوانی کنید. این کار نمونه ای از فریم های فیلم را تا زمانی که mouseup دکمه دکمه تشخیص داده شود ، ادامه می یابد و gatherDataState روی STOP_DATA_GATHER, در این مرحله حلقه جمع آوری داده به پایان می رسد.

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

541051644a45131f.gif

شما باید متن وضعیت را به روز کنید زیرا تمام تنسورها را در حافظه ذخیره می کند ، همانطور که در ضبط صفحه در بالا نشان داده شده است.

14. قطار و پیش بینی

مرحله بعدی اجرای کد برای عملکرد trainAndPredict() است ، جایی که یادگیری نقل و انتقالات در آن صورت می گیرد. بیایید نگاهی به کد بیندازیم:

script.js

async function trainAndPredict() {
  predict = false;
  tf.util.shuffleCombo(trainingDataInputs, trainingDataOutputs);
  let outputsAsTensor = tf.tensor1d(trainingDataOutputs, 'int32');
  let oneHotOutputs = tf.oneHot(outputsAsTensor, CLASS_NAMES.length);
  let inputsAsTensor = tf.stack(trainingDataInputs);
  
  let results = await model.fit(inputsAsTensor, oneHotOutputs, {shuffle: true, batchSize: 5, epochs: 10, 
      callbacks: {onEpochEnd: logProgress} });
  
  outputsAsTensor.dispose();
  oneHotOutputs.dispose();
  inputsAsTensor.dispose();
  predict = true;
  predictLoop();
}

function logProgress(epoch, logs) {
  console.log('Data for epoch ' + epoch, logs);
}

ابتدا اطمینان حاصل کنید که با تنظیم predict به false ، پیش بینی های فعلی را متوقف می کنید.

در مرحله بعد ، آرایه های ورودی و خروجی خود را با استفاده از tf.util.shuffleCombo() تغییر دهید تا اطمینان حاصل شود که سفارش باعث ایجاد مشکلی در آموزش نمی شود.

آرایه خروجی خود را ، trainingDataOutputs, تبدیل کنید تا یک tensor1d از نوع int32 باشد ، بنابراین آماده است تا در یک رمزگذاری داغ استفاده شود. این در یک متغیر به نام outputsAsTensor ذخیره می شود.

با استفاده از این متغیر outputsAsTensor به همراه حداکثر تعداد کلاس ها برای رمزگذاری ، از عملکرد tf.oneHot() استفاده کنید ، که فقط CLASS_NAMES.length است. یکی از خروجی های رمزگذاری شده شما اکنون در یک تانسور جدید به نام oneHotOutputs ذخیره می شود.

توجه داشته باشید که در حال حاضر trainingDataInputs مجموعه ای از تنسورهای ضبط شده است. به منظور استفاده از این موارد برای آموزش ، شما نیاز به تبدیل آرایه تانسور برای تبدیل شدن به یک تانسور معمولی 2D دارید.

برای انجام این کار یک عملکرد عالی در کتابخانه tensorflow.js به نام tf.stack() وجود دارد.

که مجموعه ای از تانسور را می گیرد و آنها را با هم جمع می کند تا یک تانسور بعدی بالاتری به عنوان یک خروجی تولید کند. در این حالت یک تانسور 2D بازگردانده می شود ، این یک دسته از ورودی های 1 بعدی است که هر 1024 طول دارند که دارای ویژگی های ثبت شده است ، این همان چیزی است که شما برای آموزش نیاز دارید.

در مرحله بعد ، await model.fit() برای آموزش سر مدل سفارشی باشید. در اینجا شما متغیر inputsAsTensor خود را به همراه oneHotOutputs منتقل می کنید تا داده های آموزش را برای استفاده به عنوان مثال ورودی ها و خروجی های هدف به ترتیب نشان دهید. در شیء پیکربندی برای پارامتر 3 ، shuffle به true تنظیم کنید ، از batchSize 5 استفاده کنید ، با epochs روی 10 تنظیم شده است ، و سپس یک callback برای onEpochEnd به عملکرد logProgress مشخص کنید که به زودی تعریف خواهید کرد.

سرانجام ، می توانید تانسور های ایجاد شده را دفع کنید زیرا این مدل اکنون آموزش داده شده است. سپس می توانید predict به true تنظیم کنید تا پیش بینی ها دوباره اتفاق بیفتد ، و سپس با عملکرد predictLoop() تماس بگیرید تا پیش بینی تصاویر وب کم زنده را آغاز کنید.

همچنین می توانید عملکرد logProcess() را برای ورود به سیستم آموزش تعریف کنید ، که در model.fit() در بالا استفاده می شود و این نتایج را برای کنسول بعد از هر دور از آموزش چاپ می کند.

شما تقریباً آنجا هستید! زمان اضافه کردن عملکرد predictLoop() برای پیش بینی.

حلقه پیش بینی اصلی

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

بیایید کد را بررسی کنیم:

script.js

function predictLoop() {
  if (predict) {
    tf.tidy(function() {
      let videoFrameAsTensor = tf.browser.fromPixels(VIDEO).div(255);
      let resizedTensorFrame = tf.image.resizeBilinear(videoFrameAsTensor,[MOBILE_NET_INPUT_HEIGHT, 
          MOBILE_NET_INPUT_WIDTH], true);

      let imageFeatures = mobilenet.predict(resizedTensorFrame.expandDims());
      let prediction = model.predict(imageFeatures).squeeze();
      let highestIndex = prediction.argMax().arraySync();
      let predictionArray = prediction.arraySync();

      STATUS.innerText = 'Prediction: ' + CLASS_NAMES[highestIndex] + ' with ' + Math.floor(predictionArray[highestIndex] * 100) + '% confidence';
    });

    window.requestAnimationFrame(predictLoop);
  }
}

ابتدا بررسی کنید که predict صحیح است ، به طوری که پیش بینی ها فقط پس از آموزش یک مدل انجام می شود و برای استفاده در دسترس است.

در مرحله بعد ، می توانید ویژگی های تصویر را برای تصویر فعلی درست مانند عملکرد dataGatherLoop() دریافت کنید. در اصل ، شما با استفاده از tf.browser.from pixels() ، یک قاب را از وب کم می گیرید ، آن را عادی کنید ، آن را به اندازه 224 با 224 پیکسل تغییر دهید و سپس آن داده ها را از طریق مدل Mobilenet منتقل کنید تا ویژگی های تصویر حاصل را بدست آورید.

با این حال ، اکنون می توانید از سر مدل تازه آموزش دیده خود استفاده کنید تا در واقع با عبور از imageFeatures حاصل از آن که فقط از طریق عملکرد predict() مدل آموزش دیده پیدا شده است ، پیش بینی کنید. سپس می توانید تانسور حاصل را فشار دهید تا دوباره 1 بعدی شود و آن را به متغیری به نام prediction اختصاص دهید.

با این prediction می توانید شاخصی را که دارای بالاترین مقدار با استفاده از argMax() ، پیدا کنید و سپس این تانسور حاصل را با استفاده از arraySync() به یک آرایه تبدیل کنید تا در داده های زیرین در جاوا اسکریپت دریافت کنید تا موقعیت بالاترین عنصر با ارزش را کشف کنید. این مقدار در متغیر بنام highestIndex ذخیره می شود.

همچنین می توانید با فراخوانی arraySync() در مورد تانسور prediction ، نمرات اعتماد به نفس پیش بینی واقعی را به همان روش دریافت کنید.

شما اکنون همه چیز را برای به روزرسانی متن STATUS با داده های prediction دارید. برای به دست آوردن رشته قابل خواندن بشر برای کلاس ، فقط می توانید highestIndex را در آرایه CLASS_NAMES جستجو کنید ، و سپس مقدار اعتماد به نفس را از predictionArray دریافت کنید. برای خواندن آن به عنوان یک درصد ، فقط 100 و math.floor() را ضرب کنید.

در آخر ، می توانید از window.requestAnimationFrame() برای تماس با predictionLoop() استفاده کنید تا دوباره یک بار آماده شوید ، تا طبقه بندی زمان واقعی را در جریان ویدیویی خود دریافت کنید. اگر predict به آموزش یک مدل جدید با داده های جدید دارید ، این کار ادامه false یابد.

که شما را به قطعه نهایی پازل می رساند. اجرای دکمه تنظیم مجدد.

15. دکمه تنظیم مجدد را پیاده سازی کنید

تقریبا کامل! قطعه نهایی پازل اجرای یک دکمه تنظیم مجدد برای شروع است. کد reset() در حال حاضر خالی شما در زیر است. پیش بروید و آن را به شرح زیر به روز کنید:

script.js

/**
 * Purge data and start over. Note this does not dispose of the loaded 
 * MobileNet model and MLP head tensors as you will need to reuse 
 * them to train a new model.
 **/
function reset() {
  predict = false;
  examplesCount.length = 0;
  for (let i = 0; i < trainingDataInputs.length; i++) {
    trainingDataInputs[i].dispose();
  }
  trainingDataInputs.length = 0;
  trainingDataOutputs.length = 0;
  STATUS.innerText = 'No data collected';
  
  console.log('Tensors in memory: ' + tf.memory().numTensors);
}

ابتدا با تنظیم predict false ، هر حلقه پیش بینی در حال اجرا را متوقف کنید. در مرحله بعد ، با تنظیم طول آن ، تمام مطالب موجود در آرایه examplesCount را حذف کنید ، که این یک روش مفید برای پاک کردن همه مطالب از یک آرایه است.

اکنون تمام trainingDataInputs ثبت شده فعلی را طی کنید و اطمینان حاصل کنید که dispose() ، زیرا تنشور توسط جمع کننده زباله JavaScript تمیز نمی شود.

پس از اتمام این کار ، اکنون می توانید با خیال راحت طول آرایه را روی 0 در هر دو trainingDataInputs و trainingDataOutputs آرایه ها تنظیم کنید تا این موارد را نیز پاک کنید.

سرانجام متن STATUS را روی چیزی معقول تنظیم کنید و تنش های باقی مانده در حافظه را به عنوان یک بررسی عقل چاپ کنید.

توجه داشته باشید که چند صد تن از تانسور هنوز هم در حافظه وجود خواهد داشت زیرا هم مدل Mobilenet و هم Perceptron چند لایه ای که شما تعریف کرده اید از آن دور نشده است. اگر تصمیم دارید بعد از این تنظیم مجدد دوباره آموزش دهید ، باید آنها را با داده های جدید آموزش استفاده کنید.

16. بیایید آن را امتحان کنیم

وقت آن است که نسخه بسیار شخصی خود را از دستگاه قابل آموزش تست کنید!

به پیش نمایش زنده بروید ، وب کم را فعال کنید ، حداقل 30 نمونه را برای کلاس 1 برای برخی از شیء در اتاق خود جمع کنید و سپس برای کلاس 2 برای یک شیء متفاوت ، روی قطار کلیک کنید و برای دیدن پیشرفت ، روی قطار کلیک کنید و ورود به سیستم کنسول را بررسی کنید. باید خیلی سریع تمرین کند:

bf1ac3cc5b15740.gif

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

17. تبریک می گویم

تبریک می گویم! شما به تازگی اولین مثال یادگیری انتقال خود را با استفاده از tensorflow.js در مرورگر به پایان رسانده اید.

آن را امتحان کنید ، آن را بر روی اشیاء مختلف آزمایش کنید ، ممکن است متوجه شوید که تشخیص برخی از موارد سخت تر از سایرین است ، به خصوص اگر شبیه چیز دیگری باشند. ممکن است لازم باشد کلاس ها یا داده های آموزشی بیشتری اضافه کنید تا بتوانید آنها را از هم جدا کنید.

خلاصه

در این CodeLab شما یاد گرفتید:

  1. یادگیری انتقال چیست و مزایای آن در آموزش یک مدل کامل است.
  2. چگونه می توان مدل هایی را برای استفاده مجدد از Hub TensorFlow دریافت کرد.
  3. نحوه تنظیم یک برنامه وب مناسب برای یادگیری انتقال.
  4. نحوه بارگیری و استفاده از یک مدل پایه برای تولید ویژگی های تصویر.
  5. نحوه آموزش یک پیش بینی جدید که می تواند اشیاء سفارشی را از تصاویر وب کم تشخیص دهد.
  6. نحوه استفاده از مدل های حاصل برای طبقه بندی داده ها در زمان واقعی.

بعدش چی؟

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

برای ادامه کار ، این دوره کامل را به صورت رایگان در نظر بگیرید ، که به شما نشان می دهد که چگونه می توانید 2 مدل مورد نظر خود را در این CodeLab در 1 مدل واحد برای بهره وری ترکیب کنید.

همچنین اگر بیشتر در مورد تئوری پشت برنامه اصلی آموزش ماشین قابل کنجکاو هستید ، این آموزش را بررسی کنید.

آنچه را که با ما درست می کنید به اشتراک بگذارید

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

به یاد داشته باشید که ما را در رسانه های اجتماعی با استفاده از هشتگ #madewithtfjs برچسب گذاری کنید تا فرصتی برای پروژه شما در وبلاگ TensorFlow یا حتی رویدادهای آینده ارائه شود. ما دوست داریم ببینیم چه چیزی می سازید.

وب سایت ها برای بررسی