چگونه از کتابخانه یادگیری عمیق Keras استفاده کنیم؟
شبکه‌های عمیق عصبی از پر کاربردترین حوزه‌های هوش مصنوعی هستند که قابلیت‌های کاربردی در اختیار سازمان‌ها قرار می‌دهند. با این‌حال، کاربرد شبکه‌های عمیق فراتر از نیازهای روزمره شرکت‌های عادی است و به بیان دقیق‌تر، برای مقاصد حرفه‌ای استفاده می‌شوند. یکی از قدرتمندترین کتابخانه‌هایی که برای کار و ساخت یک شبکه عصبیNeural Network در دسترس کاربران قرار دارد کتابخانه کرس» (Keras) است که در اختیار توسعه‌دهندگان پایتون قرار دارد. در این مطلب قصد داریم به شکل ساده نحوه کار با این کتابخانه را به شما نشان دهیم.

1606683296_1_0.gif

چگونه یک شبکه عصبی مصنوعی را آموزش دهیم؟

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

1. وزن‌ها به‌طور تصادفی با مقادیر نزدیک به صفر، اما غیر صفر مقداردهی اولیه می‌شوند.

2. مشاهدات مجموعه داده به لایه ورودی داده می‌شود.

3. انتشار به جلو (از چپ به راست): سلول‌های عصبی فعال می‌شوند و مقادیر پیش‌بینی شده به‌دست می‌آیند.

4. نتایج پیش‌بینی شده کامپیوتری با مقادیر واقعی مقایسه و نرخ خطا محاسبه می‌شود.

5. بازگشت به عقب (از راست به چپ): وزن‌ها تنظیم می‌شوند.

6. مراحل 1 تا 5 تکرار می‌شوند.

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

مسئله کسب‌و‌کار

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

 

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

مثل بیشتر مسائل کسب‌و‌کار، داده‌های جمع‌آشده آماده تحلیل نیستند. بنابراین متخصص داده‌کاوی باید آن‌ها را به شکلی قابل فهم برای الگوریتم تبدیل کند. با توجه به مجموعه داده بدیهی است که چند ستون با نوع داده طبقه‌ای (Categorical) وجود دارد.

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

import pandas as pd

import numpy as np

df = pd.read_csv(‘Datasets/claims/insurance_claims.csv’)

در ادامه، ستون‌های طبقه‌ای به متغیرهای ظاهری متغیرهای مجازی (Dummy Variables) تبدیل می‌شوند.

feats = [‘policy_state’,’insured_sex’,’insured_education_level’,’insured_occupation’,’insured_hobbies’,’insured_relationship’,’collision_type’,’incident_severity’,’authorities_contacted’,’incident_state’,’incident_city’,’incident_location’,’property_damage’,’police_report_available’,’auto_make’,’auto_model’,’fraud_reported’,’incident_type’]

df_final = pd.get_dummies(df,columns=feats,drop_first=True)

در دستور فوق از drop_first=True برای پیشگیری از بروز مشکل تله (Trap) در ارتباط با متغیرهای ظاهری استفاده می‌شود. به‌طور مثال، اگر d ، c ، b ، a  طبقه‌ها باشند، می‌توان d را به عنوان متغیر ظاهری حذف کرد، زیرا اگر چیزی درون b ، a و c قرار نگرفته بود، قطعا در d وجود دارد. به این اتفاق هم‌خطی چندگانه (Multicollinearity) می‌گویند. اکنون از train_test_split کتابخانه سایکیت‌لِرن برای تقسیم داده‌ها به دسته‌های آموزش (training) و آزمایش (test) استفاده می‌شود.

from sklearn.model_selection import train_test_split

در این مرحله باید مطمئن شوید ستون هنگام پیش‌بینی حذف می‌شود تا مشکل سرریز در مجموعه آموزش و آزمایش به وجود نیاورد. البته دقت کنید که نباید از مجموعه داده مشابه برای آموزش و آزمایش مدل استفاده کنید. برای دریافت آرایه  NumPyدر پایان مجموعه داده (values) تنظیم می‌شود. رویکرد فوق راهکاری است که مدل یادگیری عمیق (Deep Learning) داده‌ها را قبول می‌کند. این مرحله به این دلیل مهم است که مدل یادگیری ماشین ساخته شده در این مطلب داده‌ها را در قالب آرایه قبول کند.

X = df_final.drop([‘fraud_reported_Y’,’policy_csl’,’policy_bind_date’,’incident_date’],axis=1).values

y = df_final[‘fraud_reported_Y’].values

در ادامه، داده‌ها به مجموعه‌های آموزش و آزمایش تقسیم می‌شوند. 0.7 از داده‌ها برای آموزش و 0.3 برای تست استفاده می‌شود.

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)

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

from sklearn.preprocessing import StandardScaler

sc = StandardScaler()

X_train = sc.fit_transform(X_train)

X_test = sc.transform(X_test)

نحوه ساخت شبکه عصبی مصنوعی

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

import keras

در ادامه باید چند ماژول Keras  را وارد کنید. ماژول Sequential برای مقداردهی اولیه به شبکه عصبی عمیق و ماژول Dense برای ساخت لایه‌های شبکه عصبی مصنوعی استفاده می‌شود.

from keras.models import Sequential

from keras.layers import Dense

در ادامه، نیاز به مقداردهی اولیه به شبکه عصبی عمیق با ساخت یک نمونه از Sequential دارید. تابع Sequential یک پشته خطی از لایه‌ها را مقداردهی اولیه می‌کند تا کاربر امکان اضافه کردن لایه‌ها در آینده و با استفاده از ماژول Dense را داشته باشد.

classifier = Sequential()

اضافه کردن لایه ورودی (اولین لایه پنهان)

از روش add برای افزودن لایه‌های مختلف به شبکه عصبی استفاده می‌شود. اولین پارامتر تعداد گره‌هایی (Nodes) است که کاربر اضافه کردن به این لایه را دارد. هیچ قاعده مشخصی برای محاسبه این‌که چه تعداد گره باید اضافه شود وجود ندارد. البته، بیشتر متخصصان تعداد گره‌ها را برابر با میانگین تعداد گره‌های موجود در لایه ورودی و تعداد گره‌های لایه خروجی انتخاب می‌کنند.

به‌طور مثال، اگر پنج متغیر مستقل و یک خروجی باشد، مجموع آن‌ها محاسبه و بر دو تقسیم می‌شود که برای مثال بیان شده برابر با سه است. به‌علاوه، می‌توان با روشی که تنظیم پارامتر (Parameter Tuning) نام دارد آزمایش کرد. دومین پارامتر، kernel_initializer، تابعی برای مقداردهی اولیه به وزن‌ها است. در این مورد، از توزیع یکنواخت (Uniform Distribution) برای اطمینان از این موضوع که وزن‌ها اعداد کوچک نزدیک به صفر هستند استفاده می‌شود.

پارامتر بعدی تابع فعال‌سازی (Activation Function) است. در مثال فوق از تابع یکسوساز (Rectifier) که به‌شکل relu خلاصه شده در قالب تابع فعال‌سازی استفاده می‌شود. اغلب از این تابع برای لایه پنهان در شبکه عمیق عصبی استفاده می‌شود. پارامتر نهایی input_dim نام دارد که تعداد گره‌ها در لایه پنهان است. این پارامتر نشانگر تعداد متغیرهای مستقل است.

classsifier.add(

        Dense(3, kernel_initializer = ‘uniform’,

              activation = ‘relu’, input_dim=5))

اضافه کردن دومین لایه پنهان

اضافه کردن دومین لایه پنهان شبیه به حالت قبل است.

classsifier.add(

      Dense(3, kernel_initializer = ‘uniform’,

            activation = ‘relu’))

در این‌جا نیازی به تعیین پارامتر input_dim نیست، زیرا در اولین لایه پنهان مشخص است. در اولین لایه پنهان این متغیر مشخص شده تا به لایه اجازه دهد تا بداند انتظار چه تعدادی گره ورودی را داشته باشد. در دومین لایه پنهان، شبکه عمیق عصبی می‌داند باید انتظار چه تعداد گره ورودی را داشته باشد، بنابراین نباید به دنبال تکرار مکررات باشیم.

اضافه کردن لایه خروجی

classifier.add(

     Dense(1, kernel_initializer = ‘uniform’,

           activation = ‘sigmoid’))

اولین پارامتر باید تغییر کند، زیرا در گره خروجی انتظار یک گره داریم. به این دلیل که در این مساله، هدف مشخص کردن کلاهبرداری بودن یا نبودن یک ادعا است. این‌کار با استفاده از تابع فعال‌سازی سیگموئید (Sigmoid Activation Function) انجام می‌شود. در شرایطی که مساله دسته‌بندی و دارای بیش از دو کلاس باشد دو چیز باید تغییر کند. پارامتر اول به ۳ و تابع فعال‌سازی به سافت‌مَکس (softmax) تغییر کند. Softmax یک تابع سیگموئید اعمال شده به یک متغیر مستقل با بیش از دو دسته است.

کامپایل شبکه عصبی مصنوعی

classifier.compile(optimizer= ‘adam’,

                  loss = ‘binary_crossentropy’,

                  metrics = [‘accuracy’])

کامپایل کردن (Compiling) به معنای اعمال گرادیان کاهشی تصادفی (Stochastic Gradient Descent) به کل شبکه عصبی است. اولین پارامتر، الگوریتمی برای گرفتن مجموعه بهینه‌ای از وزن‌ها در شبکه عصبی است. انواع گوناگونی از این پارامترها وجود دارند. یکی از الگوریتم‌های موثر برای این کار آدام (Adam) است. دومین پارامتر تابع زیان (Loss Function) درون الگوریتم گرادیان کاهشی تصادفی است. با توجه به این‌که طبقه‌ها دودویی هستند از تابع زیان binary_crossentropy استفاده می‌شود. در غیر این حالت از categorical_crossentopy  استفاده می‌شد. آرگومان نهایی معیاری است که برای ارزیابی مدل استفاده می‌شود. در مثال فوق از صحت (Accuracy)  برای ارزیابی مدل استفاده می‌شود.

برازش شبکه عمیق عصبی به مجموعه آموزش

classifier.fit(X_train, y_train, batch_size = 10, epochs = 100)

X_train  متغیرهای مستقلی که برای آموزش شبکه عصبی استفاده می‌شوند را نشان می‌دهد و y_train توصیف‌کننده ستونی است که پیش‌بینی می‌شود. دوره‌ها (Epochs) نشانگر تعداد دفعاتی هستند که مجموعه داده کامل به شبکه عصبی مصنوعی هدایت می‌شود. Batch_size  تعداد مشاهداتی است که بر اساس آن وزن‌ها به‌روزرسانی می‌شوند.

پیش‌بینی با استفاده از مجموعه آموزش

y_pred = classifier.predict(X_test)

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

y_pred = (y_pred > 0.5)

در این حالت شرکت بیمه قادر به پیگیری ادعاهایی است که مشکوک نیستند و زمان بیشتری برای ارزیابی ادعاهایی دارد که به عنوان کلاهبرداری علامت‌گذاری شده‌اند.

بررسی ماتریس درهم‌ریختگی

from sklearn.metrics import confusion_matrix

cm = confusion_matrix(y_test, y_pred)

ماتریس درهم‌ریختگی (Confusion Matrix) را می‌توان به شکل در نظر گرفت که از میان ۲۰۰۰ مشاهده،  ۱۷۵ + ۱۵۵۰ مشاهده به درستی پیش‌بینی شده‌اند. در حالیکه ۴۵ + ۲۳۰ مورد اشتباه پیش‌بینی شده‌اند. می‌توان صحت را با تقسیم تعداد پیش‌بینی‌های صحیح به کل پیش‌بینی‌ها محاسبه کرد. در این مثال، ۲۰۰۰/ (۱۷۵ + ۱۵۵۰)، است که برابر با صحت ۸۶٪ می‌شود.

انجام یک پیش‌بینی

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

new_pred = classifier.predict(sc.transform(np.array([[a,b,c,d]])))

در کد بالا، d ،c ،b ،a نمایانگر ویژگی‌هایی هستند که وجود دارد.

new_pred = (new_prediction > 0.5)

با توجه به این‌که دسته‌بند (Classifier) انتظار آرایه‌های NumPy را به عنوان ورودی دارد، باید یک مشاهده منفرد به آرایه NumPy تبدیل شود و از مقیاس‌گر (Scaler) استاندارد برای مقیاس کردن آن استفاده کرد.

ارزیابی شبکه عصبی مصنوعی طراحی شده

پس از آموزش مدل برای یک یا دو بار، می‌توان متوجه شد که صحت‌های متفاوتی دریافت می‌شوند. بنابراین نمی‌توان مطمئن بود کدامیک صحیح هستند. این موضوع، مساله موازنه بایاس و واریانس (Bias Variance Trade-Off) را به وجود می‌آورد. بنابراین باید سعی کنیم مدلی آموزش داده شود که صحیح باشد و واریانس زیادی برای صحت پس از چند بار آموزش داده شدن نداشته باشد. برای حل این مساله، از اعتبارسنجی متقابل K-fold با K مساوی ۱۰ استفاده می‌شود. رویکرد فوق باعث می‌شود  مجموعه آموزش روی ۱۰ fold تنظیم شود. در ادامه، مدل روی ۹ fold آموزش داده شده و روی  fold‌های باقی‌مانده آزمایش می‌شود. از آن‌جایی که ۱۰ fold وجود دارد، سعی می‌شود این‌کار به شکل بازگشتی از طریق ۱۰ ترکیب انجام شود. هر تکرار صحتی را به دست می‌دهد. در ادامه میانگین همه صحت‌ها محاسبه و به عنوان صحت مدل استفاده می‌شود.

from keras.wrappers.scikit_learn import KerasClassifier

سپس، تابع اعتبارسنجی متقابل K-fold از scikit_learn به برنامه وارد می‌شود.

from sklearn.model_selection import cross_val_score

KerasClassifier  منتظر است یکی از آرگومان‌های آن یک تابع باشد، بنابراین نیاز به ساخت آن تابع است. هدف از این تابع ساخت معماری شبکه عصبی مصنوعی است.

def make_classifier():

    classifier = Sequential()

    classiifier.add(Dense(3, kernel_initializer = ‘uniform’, activation = ‘relu’, input_dim=5))

    classiifier.add(Dense(3, kernel_initializer = ‘uniform’, activation = ‘relu’))

    classifier.add(Dense(1, kernel_initializer = ‘uniform’, activation = ‘sigmoid’))

    classifier.compile(optimizer= ‘adam’,loss = ‘binary_crossentropy’,metrics = [‘accuracy’])

    return classifier

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

classiifier = KerasClassifier(build_fn = make_classifier,

                            batch_size=10, nb_epoch=100)

برای اعمال تابع اعتبارسنجی متقابل  K-fold از تابع cross_val_score کتابخانه scikit-learn استفاده می‌شود. تخمین‌زننده دسته‌بندی است که با استفاده از make_classifier ساخته شد و n_jobs=-1 استفاده از همه پردازنده‌های موجود را امکان‌پذیر می‌سازد. cv تعداد fold‌ها و ۱۰ یک انتخاب متداول است. cross_val_score ده صحت از ده fold آزمایش استفاده شده در محاسبات را باز می‌گرداند.

accuracies = cross_val_score(estimator = classifier,

                             X = X_train,

                             y = y_train,

                             cv = 10,

                             n_jobs = -1)

برای به دست آوردن تطابق نسبی (Relative Accuracies) میانگین صحت‌ها دریافت می‌شود.

mean = accuracies.mean()

برای محاسبه واریانس به شرح زیر عمل می‌کنیم:

variance = accuracies.var()

در این‌جا به دنبال واریانس کم بین صحت‌ها هستیم.

بیش‌برازش

بیش‌برازش (Overfitting) در یادگیری ماشین، زمانی به وجود می‌آید که مدل جزئیات و نویز موجود در مجموعه داده را می‌آموزد و در نتیجه روی مجموعه داده آزمایش ضعیف عمل می‌کند. این اتفاق هنگامی رخ می‌دهد که تفاوت زیادی بین صحت مجموعه تست و مجموعه آموزش وجود دارد یا هنگامی که واریانس بالایی هنگام اعمال اعتبارسنجی متقابل K-fold موجود باشد. در شبکه‌های عصبی مصنوعی، با این مسئله از طریق روش (Dropout Regularization) نام دارد مقابله می‌شود. Dropout Regularization با غیر فعال‌ کردن تصادفی برخی از سلول‌های عصبی در هر تکرار از آموزش برای پیشگیری از مستقل بودن بیش از اندازه آن‌ها نسبت به هم کار می‌کند.

from keras.layers import Dropout

classifier = Sequential()

classiifier.add(Dense(3, kernel_initializer = ‘uniform’, activation = ‘relu’, input_dim=5))

# Notice the dropouts

classifier.add(Dropout(rate = 0.1))

classiifier.add(Dense(6, kernel_initializer = ‘uniform’, activation = ‘relu’))

classifier.add(Dropout(rate = 0.1))

classifier.add(Dense(1, kernel_initializer = ‘uniform’, activation = ‘sigmoid’))

classifier.compile(optimizer= ‘adam’,loss = ‘binary_crossentropy’,metrics = [‘accuracy’])

در مثال فوق، می‌توان dropout را پس از اولین لایه خروجی و پس از دومین لایه پنهان انجام داد. استفاده از نرخ ۰.۱ به این معنا است که ٪۱ از سلول‌های عصبی در هر تکرار غیر فعال خواهند شد. پیشنهاد می‌شود کار با نرخ ۰.۱ انجام پذیرد. اگرچه، نباید هرگز بیش از ۰.۴ شود، زیرا مدل دچار کم‌برازش (Underfitting) می‌شود.

تنظیم پارامتر

هنگامی که صحت مطلوب به دست آمد، می‌توان پارامترها را برای گرفتن صحت بالاتر تنظیم کرد. جست‌و‌جوی Grid به کاربر اجازه می‌دهد پارامترهای گوناگون را برای به دست آوردن بهترین پارامترها محاسبه کند. اولین گام در اینجا وارد کردن ماژول GridSearchCV از  sklearn  است.

from sklearn.model_selection import GridSearchCV

به‌علاوه، نیاز به ویرایش تابع make_classifier به شکلی است که در ادامه آموزش داده شده. یک متغیر جدید به‌نام optimizer  ساخته می‌شود تا امکان افزودن بیش از یک بهینه‌ساز در متغیر params ایجاد شود.

def make_classifier(optimizer):

    classifier = Sequential()

    classiifier.add(Dense(6, kernel_initializer = ‘uniform’, activation = ‘relu’, input_dim=11))

    classiifier.add(Dense(6, kernel_initializer = ‘uniform’, activation = ‘relu’))

    classifier.add(Dense(1, kernel_initializer = ‘uniform’, activation = ‘sigmoid’))

    classifier.compile(optimizer= optimizer,loss = ‘binary_crossentropy’,metrics = [‘accuracy’])

    return classifier

در این‌جا، هنوز از KerasClassifier استفاده می‌شود، اما اندازه دسته و تعداد دوره‌ها (epochs) ارسال نمی‌شوند، زیرا پارامترهایی هستند که قرار است تنظیم شوند.

classifier = KerasClassifier(build_fn = make_classifier)

گام بعدی ساخت یک لغت‌نامه با پارامترهایی است که مشخص شده باید تنظیم شوند. در اینجا  اندازه دسته (batch size)، تعداد دوره‌ها و تابع بهینه‌ساز این پارامترها هستند. به‌علاوه از adam برای بهینه‌ساز استفاده می‌شود و یک بهینه‌ساز جدید با عنوان rmsprop نیز اضافه می‌شود. در مستندات Keras استفاده از rmsprop هنگام کار با شبکه‌های عصبی بازگشتی (Recurrent Neural Network) پیشنهاد شده است. هرچند می‌توان از آن برای این شبکه عصبی مصنوعی نیز استفاده کرد تا تاثیر آن بر بهبود نتایج را ارزیابی کرد.

params = {

    'batch_size':[20,35],

    'nb_epoch':[150,500],

    'Optimizer':['adam','rmsprop']

}

در ادامه از Grid Search برای آزمایش این پارامترها استفاده می‌شود. تابع Grid Search از تخمین‌زننده پارامترهایی که تعریف شد، معیار امتیازدهی و تعداد  k-fold‌ها را خواهد خواند.

grid_search = GridSearchCV(estimator=classifier,

                           param_grid=params,

                           scoring=’accuracy’,

                           cv=10)

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

grid_search = grid_search.fit(X_train,y_train)

می‌توان بهترین انتخاب از پارامترها را با استفاده از best_params از شی grid search دریافت کرد. به همین ترتیب از ـbest_score برای گرفتن بهترین امتیاز استفاده می‌شود.

best_param = grid_search.best_params_

best_accuracy = grid_search.best_score_

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

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

ثبت اشتراک نسخه کاغذی ماهنامه شبکه     
ثبت اشتراک نسخه آنلاین

 

کتاب الکترونیک +Network راهنمای شبکه‌ها

  • برای دانلود تنها کتاب کامل ترجمه فارسی +Network  اینجا  کلیک کنید.

کتاب الکترونیک دوره مقدماتی آموزش پایتون

  • اگر قصد یادگیری برنامه‌نویسی را دارید ولی هیچ پیش‌زمینه‌ای ندارید اینجا کلیک کنید.

ایسوس

نظر شما چیست؟