تبدیل مدل یادگیری ماشین به برنامه وب — راهنمای کاربردی

۲۶۷ بازدید
آخرین به‌روزرسانی: ۰۵ تیر ۱۴۰۲
زمان مطالعه: ۷ دقیقه
تبدیل مدل یادگیری ماشین به برنامه وب — راهنمای کاربردی

در این مطلب، روش تبدیل مدل یادگیری ماشین به برنامه وب همراه با ارائه یک مثال کامل و کاربردی و کدهای پیاده‌سازی آن، آموزش داده شده است. زیبایی «تنسورفلو دات جی‌اس» (TensorFlow.js) در آن است که کاربر می‌تواند یک مدل «یادگیری ماشین» (Machine Learning) را در «زبان برنامه‌نویسی پایتون» (Python Programming Language) با استفاده از «کرس» (Keras) یا «تنسورفلو» (Tensorflow) آموزش دهد و آن را با استفاده از TensorFlow.js در مرورگر وب مستقر کند. کاربر برای این منظور نیازی به اجرای یک سرویس خارجی برای اجرای کوئری‌ها ندارد.

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

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

شایان توجه است که سایر کدهای این پروژه از اینجا [+] در دسترس است.

  • داده‌های مورد نیاز برای آموزش مدل
  • محیط آموزش
  • پیش‌پردازش داده‌ها
  • یادگیری ماشین
  • تبدیل یک مدل کرس به Tensorflow.js
  • کنواس اچ‌تی‌ام‌ال۵
  • یکپارچه‌سازی

تصویری از این برنامه در مرورگر وب، در زیر آورده شده است.

تبدیل مدل یادگیری ماشین به برنامه وب -- راهنمای کاربردی

داده‌های مورد نیاز برای آموزش مدل

هر مدل یادگیری ماشین، نیازمند داده‌های با کیفیت (برای آموزش دیدن) است.

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

تبدیل مدل یادگیری ماشین به برنامه وب -- راهنمای کاربردی

در این مجموعه داده، ۶۰,۰۰۰ تصویر وجود دارد که همه در ابعاد $$28x28$$، به صورت سیاه و سفید و با مقادیر پیکسل از ۰ تا ۲۵۵ هستند. قطعه کد زیر، برای بارگذاری و مشاهده مجموعه داده MNIST است.

1%tensorflow_version 2.x
2from tensorflow.keras.datasets import mnist
3import matplotlib.pyplot as plt
1(X_train, y_train), (X_test, y_test) = mnist.load_data()
2print ("X_train.shape: {}".format(X_train.shape))
3print ("y_train.shape: {}".format(y_train.shape))
4print ("X_test.shape: {}".format(X_test.shape))
5print ("y_test.shape: {}".format(y_test.shape))

خروجی قطعه کدهای بالا به صورت زیر است.

X_train.shape: (60000, 28, 28)
y_train.shape: (60000,)
X_test.shape: (10000, 28, 28)
y_test.shape: (10000,)
1plt.subplot(161)
2plt.imshow(X_train[3], cmap=plt.get_cmap('gray'))
3plt.subplot(162)
4plt.imshow(X_train[5], cmap=plt.get_cmap('gray'))
5plt.subplot(163)
6plt.imshow(X_train[7], cmap=plt.get_cmap('gray'))
7plt.subplot(164)
8plt.imshow(X_train[2], cmap=plt.get_cmap('gray'))
9plt.subplot(165)
10plt.imshow(X_train[0], cmap=plt.get_cmap('gray'))
11plt.subplot(166)
12plt.imshow(X_train[13], cmap=plt.get_cmap('gray'))
13
14plt.show()

خروجی قطعه کد بالا، به صورت زیر است.

تبدیل مدل یادگیری ماشین به برنامه وب -- راهنمای کاربردی

محیط آموزش

«گوگل کُلَب» (Google Colab) به کاربران این امکان را می‌دهد که کدهای پایتون را در مرورگر بنویسند و اجرا کنند. Colab یک پلتفرم پایتون «ژوپیتر نوت‌بوک» (Jupyter Notebook) است که از پیش، با بسیاری از کتابخانه‌های یادگیری ماشین مورد نیاز کاربر، بارگذاری شده است. این گزینه روشی بی‌دردسر برای اجرای سریع پروژه‌های یادگیری ماشین است. شایان توجه است که گوگل کلب در حال حاضر دسترسی رایگان به GPU/CPU را نیز برای کاربر فراهم می‌کند.

پیش‌پردازش داده‌ها

پیش‌پردازش‌های اندکی باید روی مجموعه داده MNIST انجام شود که در ادامه بیان شده‌اند.

# Normalize Inputs from 0–255 to 0–1
x_train = x_train / 255
x_test = x_test / 255
# One-Hot Encode outputs
y_train = np_utils.to_categorical(y_train)
y_test = np_utils.to_categorical(y_test)
num_classes = 10

یادگیری ماشین

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

این مدل ساده برای گرفتن خروجی با صحت ٪۹۸ کافی است.

1x_train_simple = x_train.reshape(60000, 28 * 28).astype(‘float32’)
2x_test_simple = x_test.reshape(10000, 28 * 28).astype(‘float32’)
3model = Sequential()
4model.add(Dense(28 * 28, input_dim=28 * 28, activation=’relu’))
5model.add(Dense(num_classes, activation=’softmax’))
6model.compile(loss=’categorical_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
7model.fit(x_train_simple, y_train, validation_data=(x_test_simple, y_test), epochs=30, batch_size=200, verbose=2)

می‌توان با استفاده از یک مدل «یادگیری عمیق» (Deep Learning) صحت را تا ٪۹۹ افزایش داد.

1x_train_deep_model = x_train.reshape((60000, 28, 28, 1)).astype(‘float32’)
2x_test_deep_model = x_test.reshape((10000, 28, 28, 1)).astype(‘float32’)
3deep_model = Sequential()
4deep_model.add(Conv2D(30, (5, 5), input_shape=(28, 28, 1), activation=’relu’))
5deep_model.add(MaxPooling2D())
6deep_model.add(Conv2D(15, (3, 3), activation=’relu’))
7deep_model.add(MaxPooling2D())
8deep_model.add(Dropout(0.2))
9deep_model.add(Flatten())
10deep_model.add(Dense(128, activation=’relu’))
11deep_model.add(Dense(50, activation=’relu’))
12deep_model.add(Dense(num_classes, activation=’softmax’))
13deep_model.compile(loss=’categorical_crossentropy’, optimizer=’adam’, metrics=[‘accuracy’])
14deep_model.fit(x_train_deep_model, y_train, validation_data=(x_test_deep_model, y_test), epochs=30, batch_size=200, verbose=2)

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

تبدیل مدل Keras به Tensorflow.js

اکنون که مدل آموزش داده شد، باید به منظور استفاده از آن با Tensorflow.js، آن را تبدیل کرد. ابتدا باید مدل را در یک مدل HDF5 ذخیره کرد.

1model.save(“model.h5”)

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

تبدیل مدل یادگیری ماشین به برنامه وب -- راهنمای کاربردی

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

1!pip install tensorflowjs
2!tensorflowjs_converter --input_format keras ‘/content/model.h5’ ‘/content/model’

content/model.h5‎/ ورودی و خروجی ذخیره شده در پوشه content/model/ است.

تبدیل مدل یادگیری ماشین به برنامه وب -- راهنمای کاربردی

TensorFlow.js باید به فایل JSON (یعنی model.json) اشاره کند و نیاز به یک فایل هم‌نیا به نام «group1-shard1of1.bin» نیاز دارد. به هر دو این فایل‌ها نیاز است، بنابراین باید هر دو آن‌ها را دانلود کرد.

کنواس اچ‌تی‌ام‌ال۵

در ادامه، یک صفحه HTML ساده که از مولفه کنواس اچ‌تی‌ام‌ال۵ استفاده می‌کند باید ساخته شود. کد این صفحه HTML در ادامه آمده است.

1<!doctype html>
2<html lang="en">
3  <head>
4    <!-- Global site tag (gtag.js) - Google Analytics -->
5    <script async src="https://www.googletagmanager.com/gtag/js?id=G-H0NW5Z2MYC"></script>
6    <script>
7      window.dataLayer = window.dataLayer || [];
8      function gtag(){dataLayer.push(arguments);}
9      gtag('js', new Date());
10
11      gtag('config', 'G-H0NW5Z2MYC');
12    </script>
13    <title>Digit Recognition WebApp</title>
14    <meta name="description" content="Simple Machine Learning Model into an WebApp using TensorFlow.js">
15    <meta name="keywords" content="Machine Learning, TensorFlow.js">
16    <meta name="author" content="Carlos Aguayo">
17    <style>
18      body {
19        touch-action: none; /*https://developer.mozilla.org/en-US/docs/Web/CSS/touch-action*/
20        font-family: "Roboto";
21      }
22      h1 {
23        margin: 50px;
24        font-size: 70px;
25        text-align: center;
26      }
27      #paint {
28        border:3px solid red;
29        margin: auto;
30      }
31      #predicted { 
32        font-size: 60px;
33        margin-top: 60px;
34        text-align: center;
35      }
36      #number {
37        border: 3px solid black;
38        margin: auto;
39        margin-top: 30px;
40        text-align: center;
41        vertical-align: middle;
42      }
43      #clear {
44        margin: auto;
45        margin-top: 70px;
46        padding: 30px;
47        text-align: center;
48      }
49    </style>
50  </head>
51  <body>
52    <!--<script type="text/javascript" src="https://livejs.com/live.js"></script>-->
53    <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
54    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.5.2/dist/tf.min.js"></script>
55    <h1>Digit Recognition WebApp</h1>
56    <div id="paint">
57      <canvas id="myCanvas"></canvas>
58    </div>
59    <div id="predicted">
60      Recognized digit
61      <div id="number"></div>
62      <button id="clear">Clear</button>
63    </div>
64    <script>
65    var isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
66    if (isMobile) {
67      $('#paint').css({'width': '60%'});
68      $('#number').css({'width': '30%', 'font-size': '240px'});
69      $('#clear').css({'font-size': '50px'});
70    } else {
71      $('#paint').css({'width': '300px'});
72      $('#number').css({'width': '150px', 'font-size': '120px'});
73      $('#clear').css({'font-size': '35px'});
74    }
75
76    var cw = $('#paint').width();
77    $('#paint').css({'height': cw + 'px'});
78
79    cw = $('#number').width();
80    $('#number').css({'height': cw + 'px'});
81
82    // From https://www.html5canvastutorials.com/labs/html5-canvas-paint-application/
83    var canvas = document.getElementById('myCanvas');
84    var context = canvas.getContext('2d');
85
86    var compuetedStyle = getComputedStyle(document.getElementById('paint'));
87    canvas.width = parseInt(compuetedStyle.getPropertyValue('width'));
88    canvas.height = parseInt(compuetedStyle.getPropertyValue('height'));
89
90    var mouse = {x: 0, y: 0};
91
92    canvas.addEventListener('mousemove', function(e) {
93      mouse.x = e.pageX - this.offsetLeft;
94      mouse.y = e.pageY - this.offsetTop;
95    }, false);
96
97    context.lineWidth = isMobile ? 60 : 25;
98    context.lineJoin = 'round';
99    context.lineCap = 'round';
100    context.strokeStyle = '#0000FF';
101
102    canvas.addEventListener('mousedown', function(e) {
103      context.moveTo(mouse.x, mouse.y);
104      context.beginPath();
105      canvas.addEventListener('mousemove', onPaint, false);
106    }, false);
107
108    canvas.addEventListener('mouseup', function() {
109      $('#number').html('<img id="spinner" src="spinner.gif"/>');
110      canvas.removeEventListener('mousemove', onPaint, false);
111      var img = new Image();
112      img.onload = function() {
113        context.drawImage(img, 0, 0, 28, 28);
114        data = context.getImageData(0, 0, 28, 28).data;
115        var input = [];
116        for(var i = 0; i < data.length; i += 4) {
117          input.push(data[i + 2] / 255);
118        }
119        predict(input);
120      };
121      img.src = canvas.toDataURL('image/png');
122    }, false);
123
124    var onPaint = function() {
125      context.lineTo(mouse.x, mouse.y);
126      context.stroke();
127    };
128
129    tf.loadLayersModel('model/model.json').then(function(model) {
130      window.model = model;
131    });
132
133    // http://bencentra.com/code/2014/12/05/html5-canvas-touch-events.html
134    // Set up touch events for mobile, etc
135    canvas.addEventListener('touchstart', function (e) {
136      var touch = e.touches[0];
137      canvas.dispatchEvent(new MouseEvent('mousedown', {
138        clientX: touch.clientX,
139        clientY: touch.clientY
140      }));
141    }, false);
142    canvas.addEventListener('touchend', function (e) {
143      canvas.dispatchEvent(new MouseEvent('mouseup', {}));
144    }, false);
145    canvas.addEventListener('touchmove', function (e) {
146      var touch = e.touches[0];
147      canvas.dispatchEvent(new MouseEvent('mousemove', {
148        clientX: touch.clientX,
149        clientY: touch.clientY
150      }));
151    }, false);
152
153    var predict = function(input) {
154      if (window.model) {
155        window.model.predict([tf.tensor(input).reshape([1, 28, 28, 1])]).array().then(function(scores){
156          scores = scores[0];
157          predicted = scores.indexOf(Math.max(...scores));
158          $('#number').html(predicted);
159        });
160      } else {
161        // The model takes a bit to load, if we are too fast, wait
162        setTimeout(function(){predict(input)}, 50);
163      }
164    }
165
166    $('#clear').click(function(){
167      context.clearRect(0, 0, canvas.width, canvas.height);
168      $('#number').html('');
169    });
170    </script>
171  </body>
172</html>

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

با استفاده از مولفه HTML5 Canvas می‌توان با استفاده از موس، در بوم نوشت.

1canvas.addEventListener('mousedown', function(e) {
2  context.moveTo(mouse.x, mouse.y);
3  context.beginPath();
4  canvas.addEventListener('mousemove', onPaint, false);
5}, false);
6var onPaint = function() {
7  context.lineTo(mouse.x, mouse.y);
8  context.stroke();
9};

همچنین، Touch Events نیز به آن اضافه می‌شود تا روی موبایل نیز کار کند. touch-action نیز برای غیر فعال کردن اسکرول صفحه، اضافه می‌شود. هنگامی که قابلیت ترسیم فراهم شد، باید تصویر ترسیم شده با موس، واکشی شود. سپس، ابعاد آن به $$28x28$$ کاهش پیدا می‌کند تا با مدل آموزش دیده، تطبیق پیدا کند.

1canvas.addEventListener('mouseup', function() {
2  $('#number').html('<img id="spinner" src="spinner.gif"/>');
3  canvas.removeEventListener('mousemove', onPaint, false);
4  var img = new Image();
5  img.onload = function() {
6    context.drawImage(img, 0, 0, 28, 28);
7    data = context.getImageData(0, 0, 28, 28).data;
8    var input = [];
9    for(var i = 0; i < data.length; i += 4) {
10      input.push(data[i + 2] / 255);
11    }
12    predict(input);
13  };
14  img.src = canvas.toDataURL('image/png');
15}, false)

سپس، داده‌ها دریافت، در آرایه «input» نگهداری و در نهایت، به تابع پیش‌بینی که در ادامه تعریف خواهد شد، پاس داده می‌شوند.

1canvas.addEventListener('mouseup', function() {
2  $('#number').html('<img id="spinner" src="spinner.gif"/>');
3  canvas.removeEventListener('mousemove', onPaint, false);
4  var img = new Image();
5  img.onload = function() {
6    context.drawImage(img, 0, 0, 28, 28);
7    data = context.getImageData(0, 0, 28, 28).data;
8    var input = [];
9    for(var i = 0; i < data.length; i += 4) {
10      input.push(data[i + 2] / 255);
11    }
12    predict(input);
13  };
14  img.src = canvas.toDataURL('image/png');
15}, false);

data یک آرایه تک‌بُعدی با مقادیر RGBA است. مدل تنها مقادیر ۰ تا ۱ (یا ۰ تا ۲۵۵ در حالت سیاه و سفید) را دریافت می‌کند. با توجه به اینکه در بوم به رنگ آبی نوشته می‌شود (با موس و توسط کاربر)، می‌تواند آرایه را به ۴ بخش تقسیم و دومین عنصر هر بخش را دریافت کرد.

یکپارچه‌سازی کلیه موارد

در نهایت، باید TensorFlow.js را بارگذاری و پیش‌بینی را اجرا کرد.

1<script src=”https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.5.2/dist/tf.min.js"></script>

کاربر باید فایل‌های model.json و group1-shard1of1.bin را دانلود و در پوشه‌ای به نام model در پوشه مشابهی که فایل HTML در آن وجود دارد، ذخیره کند. هنگامی که بارگذاری انجام شد، می‌توان مدل آموزش دیده را تنها با استفاده از دستور زیر، بارگذاری کرد.

1tf.loadLayersModel(‘model/model.json’).then(function(model) {
2 window.model = model;
3});

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

1window.model.predict([tf.tensor(input).reshape([1, 28, 28, 1])]).array().then(function(scores){
2  scores = scores[0];
3  predicted = scores.indexOf(Math.max(...scores));
4  $('#number').html(predicted);
5});

تست کردن برنامه به صورت محلی کار ساده‌ای است و می‌توان یک سرور HTTP را به سادگی برای تست با پایتون راه‌اندازی کرد.

1python3 -m http.server 8080

برای تست کردن این برنامه با استفاده از تلفن هوشمند، می‌توان از یک ابزار عالی به نام ngrok استفاده کرد.

1$ ngrok http 8080

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

هنگامی که کاربر به نتایج مورد نظر خود دست یافت، می‌تواند HTML را در یک سایت میزبانی وب مستقر کند. یک گزینه راحت برای این کار، «گیت‌هاب» (Github) است.

اگر نوشته بالا برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

^^

بر اساس رای ۲ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
Towards Data Science
نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *