ساخت داشبورد گزارش دهی در پایتون — راهنمای جامع
در این مطلب، روش ساخت داشبورد گزارش دهی در پایتون مورد بررسی قرار گرفته است. در واقع، در این پست یک راهنمای گام به گام پیرامون چگونگی ساخت یک داشبورد گزارشدهی با استفاده از «دَش» (Dash) مورد بررسی قرار گرفته است که چارچوبی برای «زبان برنامهنویسی پایتون» (Python Programming Language) به منظور ساخت برنامههای کاربردی تحلیل وب محسوب میشود.
در اینجا، به جای پرداختن به مباحث پایهای برای ساخت یک برنامه Dash، به جزئیات راهنمایی پیرامون چگونگی ساخت داشبورد با جداول داده و نمودارها پرداخته شده است. داشبورد گزارشدهی به صورت یک برنامه کاربردی چند صفحهای ساخته شده است. این کار به منظور شکستن داشبورد به صفحات مختلف و در نتیجه، ارائه دادهها به صورت سازمان یافته انجام شده است. همچنین، در این صورت دادهها به طور یکباره و غافلگیر کننده نیز ارائه نمیشوند.
در هر صفحه از داشبورد، دو جدول داده در انتخابگر طیف دادهها، یک لینک دانلود دادهها و یک مجموعه از نمودارها زیر دو داشبورد وجود دارد. طی ساخت این داشبورد، با چالشهای گوناگونی مواجهه انجام شده است که در همین مطلب، چگونگی غلبه بر آنها تشریح شده است.
ساخت داشبورد گزارش دهی در پایتون
Dash یک چارچوب پایتون برای ساخت برنامههای کاربردی وب است. این چارچوب بر فراز «فلسک» (Flask)، «پلاتلی دات جیاس» (Plotly.js)، «ریاکت» (React) و «ریاکت جیاس» (React Js) ارائه شده است. این چارچوب به کاربران امکان ساخت داشبوردهایی را با استفاده از پایتون میدهد. Dash متنباز است و برنامههای آن روی مرورگر وب اجرا میشوند. در این راهنما، مبانی Dash به کاربر معرفی میشود. همچنین، فرض شده که کاربران از پیش تجربه کار با Plotly را داشتهاند.
نصب دش (Dash)
به منظور آغاز استفاده از دش، باید چندین بسته نصب شوند که در ادامه، بیان شدهاند.
- بکاند هستهای دش
- فرانتاند دش
- مولفه HTML دش
- مولفه هستهای دش
- Plotly
1pip install dash==0.21.1
2pip install dash-renderer==0.13.0
3pip install dash-html-components==0.11.0
4pip install dash-core-components==0.23.0
5pip install plotly --upgrade
لایوت برنامه دش
یک برنامه کاربردی دش معمولا ترکیبی از دو بخش است. اولین بخش لایوت است و اینکه برنامه چگونه به نظر میرسد را مشخص میکند و دومین بخش، تعاملپذیری برنامه کاربردی را مشخص میکند. دش کلاسهای HTML فراهم میکند که کاربر را قادر به تولید محتوای HTML با پایتون میسازد.
برای استفاده از این کلاسها، باید dash_core_components و dash_html_components وارد (ایمپورت | Import) شوند. کاربران میتوانند مولفه سفارشی مد نظر خود را با استفاده از «جاوااسکریپت» (JavaScript) و ریاکت جیاس بسازند. در این راستا، باید یک فایل به نام app.py با استفاده از ویرایشگر متن محبوب کاربر ساخته و سپس این پکیجها ایمپورت شوند.
1import dash
2import dash_core_components as dcc
3import dash_html_components as html
درست مانند فلسک، مقداردهی اولیه Dash با فراخوانی کلاس Dash از dash انجام میشوند. هنگامی که این کار انجام میشود، میتوان قالبی برای برنامه ساخت. از کلاس Div از dash_html_components برای ساخت یک HTML Div استفاده میشود. سپس، از مولفههای HTML برای تولید مولفههای HTML مانند H2 ،H1 و دیگر موارد استفاده میشود.
dash_html_components حاوی همه تگهای HTML میشود. به منظور ساخت یک گراف روی این الگو، از کلاس گراف از dash_core_components استفاده میشود. Graph بصریسازیهای داده را با استفاده از plotly.js رندر میکند. کلاس گراف نیاز به یک شی شکل با data برای رسم شدن و جزئیات layout دارد. همچنین، Dash به کاربر امکان قالببندیهایی مانند تغییر رنگ پسزمینه و متن را میدهد. میتوان پسزمینه را با استفاده از خصیصه style و پاس دادن شی با رنگ خاص مد نظر کاربر، ارائه کرد.
در این مثال، یک دیکشنری رنگ با پسزمینه و رنگ متنی که مد نظر است، تعریف شدهاند. به طور مشابه، میتوان پسزمینه لایوت را با استفاده از خصیصه plot_bgcolor تغییر داد. در HTML، خصوصیات قالب با استفاده از نقطه ویرگول تعیین میشود، ولی در Dash، یک دیکشنری برای این کار تهیه شده است. کلیدهای موجود در دیکشنری به صورت «نگارش شتری» (camelCased) هستند. به عنوان مثالی از این موارد میتوان به textAlign، برای عبارت text-align اشاره کرد. به جای استفاده از کلاسهایی مانند آنچه در HTML وجود دارد، در Dash از className استفاده میشود.
1app = dash.Dash()
2colors = {
3 'background': '#111111',
4 'text': '#7FDBFF'
5}
6app.layout = html.Div(style={'backgroundColor': colors['background']}, children=[
7 html.H1(
8 children='Hello Dash',
9 style={
10 'textAlign': 'center',
11 'color': colors['text']
12 }
13 ),
14 html.Div(children='Dash: A web application framework for Python.', style={
15 'textAlign': 'center',
16 'color': colors['text']
17 }),
18 dcc.Graph(
19 id='Graph1',
20 figure={
21 'data': [
22 {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'},
23 {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'},
24 ],
25 'layout': {
26 'plot_bgcolor': colors['background'],
27 'paper_bgcolor': colors['background'],
28 'font': {
29 'color': colors['text']
30 }
31 }
32 }
33 )
34])
به منظور نمایش دادن بصریسازیها، نیاز به اجرای وبسرور درست به صورتی است که در Flask انجام میشود. باید به خاطر داشت که Dash بر فراز Flask ساخته شده است. همچنین، debug روی true تنظیم میشود تا اطمینان حاصل شود که در هر بار که تغییرات انجام میشوند، نیازی به رفرش کردن سرور نیست.
1if __name__ == '__main__':
2 app.run_server(debug=True)
سپس، به ترمینال رفته و سرور با نوشتن کد python app.py زیر راهاندازی میشود. این کار موجب میشود وب سرور در http://127.0.0.1:8050/ اجرا شود. اکنون میتوان در اینجا داشبورد ساخته شده را مشاهده کرد.
ساخت نمودارهای نقطهای
به منظور ترسیم یک نمودار نقطهای، مولفههای نرمال Dash به صورتی که پیش از این نیز انجام شد، وارد (Import) میشوند. همچنین، نیاز به وارد (ایمپورت) کردن graph_objs از Plotly به منظور ترسیم نمودار نقطهای است. چنانکه پیش از این اشاره شد، از کلاس Div و مولفههای Graph از Dash به منظور انجام این کار استفاده میشود.
مولفه Graph شی شکل را که حاوی دادههایی پیرامون دادهها و توصیفات قالب است دریافت میکند. نمودار نقطهای با استفاده از خصوصیت graph_objs ترسیم میشود. به منظور حصول اطمینان از اینکه نمودار یک نمودار نقطهای است، خصیصه mode پاس داده و به عنوان مارکر تنظیم میشود. در غیر این صورت، خطوطی روی نمودار وجود خواهد داشت.
1import dash
2import dash_core_components as dcc
3import dash_html_components as html
4import pandas as pd
5import plotly.graph_objs as go
6
7app = dash.Dash()
8
9df = pd.read_csv(
10 'https://gist.githubusercontent.com/chriddyp/' +
11 '5d1ea79569ed194d432e56108a04d188/raw/' +
12 'a9f9e8076b837d541398e999dcbac2b2826a81f8/'+
13 'gdp-life-exp-2007.csv')
14
15
16app.layout = html.Div([
17 dcc.Graph(
18 id='life-exp-vs-gdp',
19 figure={
20 'data': [
21 go.Scatter(
22 x=df[df['continent'] == i]['gdp per capita'],
23 y=df[df['continent'] == i]['life expectancy'],
24 text=df[df['continent'] == i]['country'],
25 mode='markers',
26 opacity=0.8,
27 marker={
28 'size': 15,
29 'line': {'width': 0.5, 'color': 'white'}
30 },
31 name=i
32 ) for i in df.continent.unique()
33 ],
34 'layout': go.Layout(
35 xaxis={'type': 'log', 'title': 'GDP Per Capita'},
36 yaxis={'title': 'Life Expectancy'},
37 margin={'l': 40, 'b': 40, 't': 10, 'r': 10},
38 legend={'x': 0, 'y': 1},
39 hovermode='closest'
40 )
41 }
42 )
43])
44
45if __name__ == '__main__':
46 app.run_server()
مارکداون (Markdown)
گاهی ممکن است که کاربر نیاز به استفاده از میزان زیادی متن در داشبورد خود داشته باشد.
این کار را میتوان با استفاده از خصیصه Markdown در dash_core_components به صورتی انجام داد که در زیر نمایش داده شده است.
1import dash
2import dash_core_components as dcc
3import dash_html_components as html
4
5app = dash.Dash()
6
7markdown_text = '''
8### Dash and Markdown
9A lot of text
10'''
11
12app.layout = html.Div([
13 dcc.Markdown(children=markdown_text)
14])
15
16if __name__ == '__main__':
17 app.run_server()
مولفههای هستهای
در ادامه، برخی از dash_core_components مورد بررسی قرار میگیرند که کاربر هنگام کار با Dash با آنها مواجه میشود. همچنین، میتوان یک منو کشویی را به صورتی که در ادامه نمایش داده شده است، تولید کرد. این کار با فراخوانی Dropdown off dash_core_components و پاس دادن گزینهها به صورت یک لیست از دیکشنریها انجام میشود.
میتوان مقدار پیشفرض را با استفاده از خصیصه values و پاس دادن گزینه پیشفرض انجام داد.
1 dcc.Dropdown(
2 options=[
3 {'label': 'New York City', 'value': 'NYC'},
4 {'label': u'Montréal', 'value': 'MTL'},
5 {'label': 'San Francisco', 'value': 'SF'}
6 ],
7 value='MTL'
8 )
ساخت یک منو کشویی multi-select مشابه با بالا است. تنها تغییرات لازم آن است که خصیصه multi روی true تنظیم شود، زیرا به صورت پیشفرض، False است. سپس، کاربر میتواند اقلامی که تمایل دارد چندتایی انتخاب شوند را به صورت پیشفرض با تعیین خصیصه values مشخص کند.
1html.Label('Multi-Select Dropdown'),
2 dcc.Dropdown(
3 options=[
4 {'label': 'New York City', 'value': 'NYC'},
5 {'label': u'Montréal', 'value': 'MTL'},
6 {'label': 'San Francisco', 'value': 'SF'}
7 ],
8 value=['MTL', 'SF'],
9 multi=True
10 )
«دکمه رادیویی» (Radio Buttons) را میتوان با استفاده از خصیصه RadioItems تولید کرد. سپس، گزینهها به صورت لیستی از دیکشنریها پاس داده میشوند. همچنین، میتوان یک مقدار پیشفرض را با تعیین خصیصه values تنظیم کرد.
1 html.Label('Multi-Select Dropdown'),
2 dcc.Dropdown(
3 options=[
4 {'label': 'New York City', 'value': 'NYC'},
5 {'label': u'Montréal', 'value': 'MTL'},
6 {'label': 'San Francisco', 'value': 'SF'}
7 ],
8 value=['MTL', 'SF'],
9 multi=True
10 )
دکمههای رادیویی را میتوان با استفاده از خصیصه RadioItems ساخت. سپس، گزینهها به عنوان لیستی از دیکشنریها پاس داده میشوند. همچنین، میتوان یک مقدار پیشفرض را با تعیین خصیصه values تنظیم کرد.
1html.Label('Radio Items'),
2 dcc.RadioItems(
3 options=[
4 {'label': 'New York City', 'value': 'NYC'},
5 {'label': u'Montréal', 'value': 'MTL'},
6 {'label': 'San Francisco', 'value': 'SF'}
7 ],
8 value='MTL'
9 )
برای تولید چکباکسها، خصیصه Checklist از dash_core_components فراخوانی میشود. گزینهها و مقادیر پیشفرض به صورت بالا پاس داده میشود.
1 html.Label('Checkboxes'),
2 dcc.Checklist(
3 options=[
4 {'label': 'New York City', 'value': 'NYC'},
5 {'label': u'Montréal', 'value': 'MTL'},
6 {'label': 'San Francisco', 'value': 'SF'}
7 ],
8 values=['MTL', 'SF']
9 )
قطعا نیاز به وارد کردن متن در برنامهکاربردی خواهد بود. برای تولید این متن، از خصیصه Input استفاده میشود. با استفاده از تگ Html Label، کاربر میتواند یک برچسب برای فیلد ورودی بسازد. با استفاده از خصیصه values، میتوان متنی را در فیلد تعیین و برای مشخص کردن نوع از type استفاده کرد. بدین شکل مشخص شود که آیا یک فیلد متنی، عدد و یا دیگر موارد است.
1html.Label('Text Box'),
2 dcc.Input(value='MTL', type='text')
فراخوانی کمک
از آنجا که مولفههای Dash اعلانی هستند، فراخوانی دستور کمک روی هر یک از آنها برای آن مولفه کمک ایجاد میکند.
1help(dcc.Input)
تعاملپذیری
اکنون به چگونگی تعاملپذیر کردن برنامه Dash پرداخته شده است. به منظور انجام این کار، نیاز به وارد (import) کردن ورودی و خروجی از dash.dependencies وجود دارد. نباید این مورد را با ورودی HTML اشتباه گرفت، زیرا این دو مورد متفاوت هستند. ورودی HTML از طریق مولفه هستهای Dash وارد میشود.
در ادامه، یک متن ورودی ساخته میشود و فراخوانی به آن مقید میشود؛ به طوری که هر بار فرد چیزی در جعبه متن نوشت، my-div به صورت آنی به روز رسانی شود. به منظور انجام این کار، Dash یک برنامه دکوراتور app@ دارد که امکان مقید کردن یک تابع فراخوانی برای my-div و یک فیلد ورودی HTML را فراهم میکند. شایان توجه است که از دکوراتور پیش از آنکه تابع update_output_div اعلان شود، استفاده میشود.
1import dash
2import dash_core_components as dcc
3import dash_html_components as html
4from dash.dependencies import Input, Output
5
6app = dash.Dash()
7
8app.layout = html.Div([
9 dcc.Input(id='my-id', value='Dash App', type='text'),
10 html.Div(id='my-div')
11])
12
13
14@app.callback(
15 Output(component_id='my-div', component_property='children'),
16 [Input(component_id='my-id', component_property='value')]
17)
18def update_output_div(input_value):
19 return 'You\'ve entered "{}"'.format(input_value)
20
21
22if __name__ == '__main__':
23 app.run_server()
احراز هویت
Dash امکان احراز هویت را از طریق یک بسته جدا به اسم dash-auth فراهم میکند. این بسته، دو حالت از احراز هویت HTTP Basic Auth و Plotly OAuth را فراهم میکند. در Basic Auth، مجموعهای از نامهای کاربری و رمزهای عبور در برنامه کاربردی به صورت کد سخت قرار میگیرند. این روش چالشهایی دارد مانند آنکه کاربر نمیتواند از برنامه خارج شود، نمیتواند حساب کاربری بسازد و یا اطلاعات خود را به روز رسانی کند و توسعهدهنده مسئول ذخیرهسازی امن نامهای کاربری و رمزهای عبور در برنامه خودش است.
Plotly OAuth امکان احراز هویت را از طریق حساب کاربری آنلاین Plotly خود فراهم میکند و رایگان نیست. برای راهاندازی Basic Auth باید به صورت زیر عمل کرد. ابتدا، بستههای لازم نصب میشوند.
1pip install dash==0.21.1
2pip install dash-auth==1.0.0
پس از آن، جفت نام کاربری و رمز عبوری که کاربر تمایل دارد در برنامه خود داشته باشد باید تنظیم شوند.
1VALID_USERNAME_PASSWORD_PAIRS = [
2 ['hello', 'world']
3]
ابزار dash_auth.BasicAuth از Dash هنگامی که جفت نامکاربری و رمز عبور تنظیم شد، مراقب احراز هویت است. تنها کاری که توسعهدهنده باید انجام دهد پاس دادن password pairs و نام برنامه کاربردی به dash_auth.BasicAuth است.
1app = dash.Dash('auth')
2auth = dash_auth.BasicAuth(
3 app,
4 VALID_USERNAME_PASSWORD_PAIRS
5)
میزبانی داشبوردها در Heroku
میزبانی داشبورد در Heroku کار نسبتا آسانی است، اما چندین مرحله دارد. ابتدا، یک پوشه ساخته میشود که همه فایلهای پروژه در آن نگهداری میشود. کاربر میتواند این کار را در سیستمعامل اوبونتو با استفاده از دستور mkdir انجام دهد.
1$ mkdir my_dash_app
2$ cd my_dash_app
سپس، اولیهسازی پوشه با git و virtualenv انجام میشود. گیت برای کنترل نسخه استفاده میشود و virtualenv ما را قادر به ساخت یک محیط مجازی برای نگه داشتن همه وابستگیهای پایتون میکند. پس از ساخت محیط، باید آن را با استفاده از دستور source فعال کرد.
1$ git init
2$ virtualenv venv
3$ source venv/bin/activate
سپس، همه بستههای مورد نیاز برای برنامه Dash به صورت زیر نصب میشوند.
1$ pip install dash
2$ pip install dash-renderer
3$ pip install dash-core-components
4$ pip install dash-html-components
5$ pip install plotly
به منظور خدمترسانی به برنامه کاربردی Dash، نیاز به یک وبسرور پایتون است. در اینجا هرگز از سرور توسعه «فلسک» (Flask) در تولید استفاده نمیشود. از وب سرور Gunicorn برای این تابع استفاده میشود. نصب آن به صورت زیر انجام میشود.
1$ pip install gunicorn
سپس، نیاز به ساخت چند فایل در پوشه است:
- app.py باید در جایی ساخته شود که برنامه dash کدنویسی میشود.
- یک gitignore. باید به منظور حصول اطمینان از این ساخته شود که فایلهای غیر لازم در تولید قرار نمیگیرد.
- یک فایل requirements.txt ساخته شود که حاوی همه وابستگیهای پایتون و نسخههای آن است.
- یک پروفایل برای استقرار ساخته شود.
اکنون، کد زیر باید به app.py اضافه شود. این تنها یک نمونه کد است و افراد میتوانند داشبورد Dash مورد نظر خودشان را استفاده کنند.
1import os
2import dash
3import dash_core_components as dcc
4import dash_html_components as html
5app = dash.Dash(__name__)
6server = app.server
7app.layout = html.Div([
8 html.H2('Hello World'),
9 dcc.Dropdown(
10 id='dropdown',
11 options=[{'label': i, 'value': i} for i in ['LA', 'NYC', 'MTL']],
12 value='LA'
13 ),
14 html.Div(id='display-value')
15])
16@app.callback(dash.dependencies.Output('display-value', 'children'),
17 [dash.dependencies.Input('dropdown', 'value')])
18def display_value(value):
19 return 'You have selected "{}"'.format(value)
20if __name__ == '__main__':
21 app.run_server(debug=True)
باید به خاطر داشت که برنامه Dash، در هسته خود، یک برنامه Flask نیز هست. برای هدف استقرار، نیاز به دسترسی به نمونههای برنامه کاربردی فلسک است. Dash کاربر را قادر به انجام این کار با استفاده از app.server میسازد.
1app = dash.Dash(__name__)
2server = app.server
اکنون، توسعهدهنده باید فایلهایی که نمیخواهد در تولید قرار بگیرد را به فایل gitignore. اضافه کند. محتوای مربوط به یک فایل پایتون gitignore. خوب، در ادامه آمده است.
1# Byte-compiled / optimized / DLL files
2__pycache__/
3*.py[cod]
4*$py.class
5
6# C extensions
7*.so
8
9# Distribution / packaging
10.Python
11build/
12develop-eggs/
13dist/
14downloads/
15eggs/
16.eggs/
17lib/
18lib64/
19parts/
20sdist/
21var/
22wheels/
23pip-wheel-metadata/
24share/python-wheels/
25*.egg-info/
26.installed.cfg
27*.egg
28MANIFEST
29
30# PyInstaller
31# Usually these files are written by a python script from a template
32# before PyInstaller builds the exe, so as to inject date/other infos into it.
33*.manifest
34*.spec
35
36# Installer logs
37pip-log.txt
38pip-delete-this-directory.txt
39
40# Unit test / coverage reports
41htmlcov/
42.tox/
43.nox/
44.coverage
45.coverage.*
46.cache
47nosetests.xml
48coverage.xml
49*.cover
50*.py,cover
51.hypothesis/
52.pytest_cache/
53
54# Translations
55*.mo
56*.pot
57
58# Django stuff:
59*.log
60local_settings.py
61db.sqlite3
62db.sqlite3-journal
63
64# Flask stuff:
65instance/
66.webassets-cache
67
68# Scrapy stuff:
69.scrapy
70
71# Sphinx documentation
72docs/_build/
73
74# PyBuilder
75target/
76
77# Jupyter Notebook
78.ipynb_checkpoints
79
80# IPython
81profile_default/
82ipython_config.py
83
84# pyenv
85.python-version
86
87# pipenv
88# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89# However, in case of collaboration, if having platform-specific dependencies or dependencies
90# having no cross-platform support, pipenv may install dependencies that don't work, or not
91# install all needed dependencies.
92#Pipfile.lock
93
94# PEP 582; used by e.g. github.com/David-OConnor/pyflow
95__pypackages__/
96
97# Celery stuff
98celerybeat-schedule
99celerybeat.pid
100
101# SageMath parsed files
102*.sage.py
103
104# Environments
105.env
106.venv
107env/
108venv/
109ENV/
110env.bak/
111venv.bak/
112
113# Spyder project settings
114.spyderproject
115.spyproject
116
117# Rope project settings
118.ropeproject
119
120# mkdocs documentation
121/site
122
123# mypy
124.mypy_cache/
125.dmypy.json
126dmypy.json
127
128# Pyre type checker
129.pyre/
اکنون، اطلاعات توسعه در Procfile تعیین میشود. از متغیرهای وب برای تعیین سرور به عنوان gunicorn استفاده میشود. همچنین، جزئیات برنامه کاربردی با به کارگیری متغیر app تعیین میشود. app به نام فایل app.py و سرور آن به متغیر server درون آن فایل ارجاع دارد.
1web: gunicorn app:server
همچنین، نیاز به نوشتن وابستگیهای پایتون در فایل نیازمندیها است. Heroku آنها را در طول استقرار نصب میکند.
1pip freeze > requirements.txt
2
در گامهای بعدی فرض میشود که کاربر یک حساب کاربری Heroku دارد و Heroku CLI را نصب کرده است. در صورتی که این موارد توسط کاربر نصب نشدهاند، باید به وبسایت Heroku مراجعه و آنها را تنظیم کند. گام بعدی، ساخت یک برنامه کاربردی Heroku در ترمینال و افزودن همه بستههای برنامه کاربردی به آن است. هنگامی که تغییرات وارد شدند، باید برنامه کاربردی را به Heroku Master پوش (Push) کرد. خروجی این دستور دارای لینکی به برنامه کاربردی Dash زنده ساخته شده توسط کاربر در Heroku است.
1$ heroku create your-app-name
2$ git add .
3$ git commit -m 'Your-commit-message'
4$ git push heroku master
این مطلب در واقع مقدمهای پیرامون چگونگی ساخت یک داشبورد با پایتون استفاده از Dash توسط Plotly بود.
اگر نوشته بالا برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای آمار، احتمالات و دادهکاوی
- آموزش هوش مصنوعی
- مجموعه آموزشهای برنامه نویسی پایتون (Python)
- داده کاوی (Data Mining) — از صفر تا صد
- یادگیری علم داده (Data Science) با پایتون — از صفر تا صد
- معرفی منابع جهت آموزش یادگیری عمیق (Deep Learning) — راهنمای کامل
- آموزش استیمول سافت | کامل، رایگان و پروژه محور — به زبان ساده
^^