.NET و TensorFlow وطواحين الهواء في Kaggle - تبدأ الرحلة

هذه سلسلة من المقالات حول رحلتي المستمرة إلى الغابة المظلمة لمسابقات Kaggle كمطور .NET.

سوف أركز على الشبكات العصبية النقية (تقريبًا) في هذا المقال والمقالات التالية. وهذا يعني أن معظم الأجزاء المملة من إعداد مجموعة البيانات ، مثل ملء القيم المفقودة ، واختيار الميزة ، وتحليل القيم المتطرفة ، إلخ. سيتم تخطي عمدا.

ستكون الحزمة التقنية C # + TensorFlow tf.keras API. اعتبارا من اليوم سوف يتطلب أيضا ويندوز. قد تحتاج النماذج الأكبر حجمًا في مقالات المستقبل إلى وحدة معالجة الرسومات المناسبة لوقت التدريب لتظل عاقلًا.

دعونا التنبؤ أسعار العقارات!


أسعار المنازل هي منافسة كبيرة للمبتدئين. مجموعة بياناتها صغيرة ، ولا توجد قواعد خاصة ، ولوحة المتصدرين العامة بها العديد من المشاركين ، ويمكنك إرسال ما يصل إلى 4 مقالات في اليوم.

سجل في Kaggle ، إذا لم تكن قد فعلت ذلك بعد ، انضم إلى هذه المسابقة ، وقم بتنزيل البيانات. الهدف هو التنبؤ بسعر البيع (عمود SalePrice) للإدخالات في test.csv . يحتوي الأرشيف على train.csv ، الذي يحتوي على حوالي 1500 إدخال مع سعر بيع معروف للتدريب عليه. سنبدأ بتحميل مجموعة البيانات هذه واستكشافها قليلاً قبل الدخول في الشبكات العصبية.

تحليل بيانات التدريب


هل قلت إننا سنتخطى إعداد مجموعة البيانات؟ كذبت! عليك إلقاء نظرة مرة واحدة على الأقل.

لدهشتي ، لم أجد طريقة سهلة لتحميل ملف .csv في مكتبة فئة .NET القياسية ، لذلك قمت بتثبيت حزمة NuGet ، تسمى CsvHelper . لتبسيط معالجة البيانات ، حصلت أيضًا على حزمة تمديد LINQ المفضلة الجديدة.

تحميل بيانات .csv في DataTable
static DataTable LoadData(string csvFilePath) { var result = new DataTable(); using (var reader = new CsvDataReader(new CsvReader(new StreamReader(csvFilePath)))) { result.Load(reader); } return result; } 


ML.NET
إن استخدام DataTable للتدريب على معالجة البيانات يعد في الواقع فكرة سيئة.

من المفترض أن يكون لدى .NET.NET تحميل .csv والعديد من عمليات فصل البيانات واستكشافها. ومع ذلك ، لم يكن جاهزًا لهذا الغرض بالتحديد ، عندما دخلت مسابقة أسعار المنازل.


تبدو البيانات هكذا (فقط عدد قليل من الصفوف والأعمدة):

معرفMSSubClassميسونينجLotFrontageلوتاريا
160RL658450
220RL809600
360RL6811250
470RL609550


بعد تحميل البيانات ، نحتاج إلى إزالة عمود الرقم التعريفي ، لأنه لا يرتبط فعليًا بأسعار المنزل:

 var trainData = LoadData("train.csv"); trainData.Columns.Remove("Id"); 

تحليل أنواع بيانات الأعمدة


لا يستنتج DataTable تلقائيًا أنواع بيانات الأعمدة ، ويفترض أن الأمر كله عبارة عن سلاسل. وبالتالي فإن الخطوة التالية هي تحديد ما لدينا بالفعل. لكل عمود قمت بحساب الإحصائيات التالية: عدد القيم المتميزة ، وعددها أعداد صحيحة ، وعددها عبارة عن أرقام نقطية عائمة (سيتم ربط رمز مصدر بكل أساليب المساعدة في نهاية المقالة):

 var values = rows.Select(row => (string)row[column]); double floats = values.Percentage(v => double.TryParse(v, out _)); double ints = values.Percentage(v => int.TryParse(v, out _)); int distincts = values.Distinct().Count(); 

الأعمدة الرقمية


اتضح أن معظم الأعمدة هي في الواقع ، ولكن نظرًا لأن الشبكات العصبية تعمل في الغالب على الأرقام العائمة ، فإننا سنحولها إلى أزواج على أي حال.

الأعمدة الفئوية


تصف الأعمدة الأخرى فئات الممتلكات المعروضة للبيع. لا أحد منهم لديه الكثير من القيم المختلفة ، وهو أمر جيد. لاستخدامها كمدخلات لشبكتنا العصبية المستقبلية ، يجب تحويلها إلى الضعف أيضًا.

في البداية ، قمت ببساطة بتعيين أرقام من 0 إلى مميزةValueCount - 1 لهم ، ولكن هذا لا معنى له ، حيث لا يوجد تقدم في الواقع من "الواجهة: الأزرق" إلى "الواجهة: الأخضر" إلى "الواجهة: أبيض". لقد غيّرت ذلك مبكرًا إلى ما يسمى الترميز الساخن ، حيث تحصل كل قيمة فريدة على عمود إدخال منفصل. على سبيل المثال ، تصبح "الواجهة: أزرق" [1،0،0] ، وتصبح "الواجهة: بيضاء" [0،0،1].

الحصول عليها جميعا معا


انتاج كبير من استكشاف البيانات
CentralAir: 2 القيم ، ints: 0.00٪ ، يطفو: 0.00٪
الشارع: قيمتان ، بالأرقام: 0.00٪ ، العائم: 0.00٪
المرافق: 2 القيم ، ints: 0.00 ٪ ، يطفو: 0.00 ٪
الزقاق: 3 قيم ، بالأرقام: 0.00٪ ، يطفو: 0.00٪
BsmtHalfBath: 3 قيم ، ints: 100.00٪ ، يطفو: 100.00٪
HalfBath: 3 قيم ، intts: 100.00 ٪ ، يطفو: 100.00 ٪
LandSlope: 3 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
PavedDrive: 3 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
BsmtFullBath: 4 قيم ، intts: 100.00٪ ، يطفو: 100.00٪
ExterQual: 4 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
المواقد: 4 قيم ، بالأرقام: 100.00٪ ، العوامات: 100.00٪
FullBath: 4 قيم ، intts: 100.00٪ ، يطفو: 100.00٪
GarageFinish: 4 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
KitchenAbvGr: 4 قيم ، بالأرقام: 100.00٪ ، تعوم: 100.00٪
KitchenQual: 4 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
LandContour: 4 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
شكل Lot: 4 قيم ، intts: 0.00٪ ، تعويم: 0.00٪
PoolQC: 4 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
نوع المبنى: 5 قيم ، بالأرقام: 0.00٪ ، العائم: 0.00٪
BsmtCond: 5 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
BsmtExposure: 5 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
BsmtQual: 5 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
ExterCond: 5 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
السياج: 5 قيم ، بالأرقام: 0.00٪ ، العائم: 0.00٪
GarageCars: 5 قيم ، intts: 100.00 ٪ ، يطفو: 100.00 ٪
تدفئة QC: 5 القيم ، ints: 0.00 ٪ ، يطفو: 0.00 ٪
LotConfig: 5 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
MasVnrType: 5 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
MiscFeature: 5 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
MSZoning: 5 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
YrSold: 5 قيم ، intts: 100.00 ٪ ، يطفو: 100.00 ٪
الكهربائية: 6 قيم ، بالأرقام: 0.00٪ ، العربات: 0.00٪
FireplaceQu: 6 قيم ، ints: 0.00٪ ، يطفو: 0.00٪
الأساس: 6 قيم ، بالأرقام: 0.00٪ ، يطفو: 0.00٪
GarageCond: 6 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
GarageQual: 6 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
التسخين: 6 قيم ، بالأرقام: 0.00٪ ، يطفو: 0.00٪
RoofStyle: 6 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
بيعالشرط: 6 القيم ، ints: 0.00٪ ، يطفو: 0.00٪
BsmtFinType1: 7 قيم ، ints: 0.00٪ ، يطفو: 0.00٪
BsmtFinType2: 7 قيم ، ints: 0.00٪ ، يطفو: 0.00٪
وظيفية: 7 قيم ، عدد الأحرف: 0.00٪ ، عدد الأسهم: 0.00٪
GarageType: 7 قيم ، intts: 0.00٪ ، تعويم: 0.00٪
BedroomAbvGr: 8 قيم ، intts: 100.00 ٪ ، يطفو: 100.00 ٪
الشرط 2: 8 قيم ، بالأرقام: 0.00٪ ، العائم: 0.00٪
HouseStyle: 8 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
PoolArea: 8 قيم ، intts: 100.00 ٪ ، يطفو: 100.00 ٪
RoofMatl: 8 قيم ، intts: 0.00٪ ، يطفو: 0.00٪
الشرط 1: 9 قيم ، عدد الأحرف: 0.00٪ ، عدد الأسهم: 0.00٪
الإجماليالكوند: 9 قيم ، بالأرقام: 100.00٪ ، العائم: 100.00٪
نوع بيع: 9 قيم ، ints: 0.00 ٪ ، يطفو: 0.00 ٪
عموماالقيمة: 10 القيم ، ints: 100.00 ٪ ، يطفو: 100.00 ٪
MoSold: 12 قيمة ، intts: 100.00 ٪ ، يطفو: 100.00 ٪
TotRmsAbvGrd: 12 قيمة ، ints: 100.00٪ ، تعويم: 100.00٪
المظهر الخارجي: 15 قيمة ، القيمة المضافة: 0.00٪ ، الطفو: 0.00٪
MSSubClass: 15 قيمة ، intts: 100.00٪ ، تعويم: 100.00٪
الخارجي 2: 16 القيم ، ints: 0.00٪ ، يطفو: 0.00٪
3SsNPorch: 20 قيمة ، بالأرقام: 100.00٪ ، تعوم: 100.00٪
MiscVal: 21 قيمة ، intts: 100.00٪ ، تعويم: 100.00٪
LowQualFinSF: 24 قيمة ، بالأرقام: 100.00٪ ، تعوم: 100.00٪
الجوار: 25 قيمة ، بالأرقام: 0.00٪ ، العائم: 0.00٪
YearRemodAdd: 61 قيمة ، intts: 100.00 ٪ ، يطفو: 100.00 ٪
ScreenPorch: 76 القيم ، ints: 100.00 ٪ ، يطفو: 100.00 ٪
GarageYrBlt: 98 قيمًا ، بالأرقام: 94.45٪ ، العوامات: 94.45٪
قيمة الواجهة: 111 قيمة ، القيمة المضافة: 82.26٪ ، الطفو: 82.26٪
بنيت عام: 112 القيم ، ints: 100.00 ٪ ، يطفو: 100.00 ٪
EnclosedPorch: 120 قيمة ، intts: 100.00٪ ، تعوم: 100.00٪
BsmtFinSF2: 144 قيمًا ، ints: 100.00٪ ، يطفو: 100.00٪
OpenPorchSF: 202 قيمة ، intts: 100.00٪ ، تعويم: 100.00٪
WoodDeckSF: 274 قيمة ، intts: 100.00 ٪ ، يطفو: 100.00 ٪
MasVnrArea: 328 قيمة ، intts: 99.45 ٪ ، يطفو: 99.45 ٪
2ndFlrSF: 417 القيم ، ints: 100.00 ٪ ، يطفو: 100.00 ٪
عدد الكراجات: 441 قيمة ، 100٪ ، تعويم: 100.00٪
BsmtFinSF1: 637 القيم ، ints: 100.00 ٪ ، يطفو: 100.00 ٪
سعر البيع: 663 القيم ، ints: 100.00 ٪ ، يطفو: 100.00 ٪
إجمالي BsmtSF: 721 قيمة ، intts: 100.00٪ ، تعويم: 100.00٪
1stFlrSF: 753 القيم ، ints: 100.00 ٪ ، يطفو: 100.00 ٪
BsmtUnfSF: قيم 780 ، بالأرقام: 100.00٪ ، تعوم: 100.00٪
GrLivArea: 861 قيمة ، intts: 100.00٪ ، تعويم: 100.00٪
قيمة العقد: 1073 قيمة ، القيمة المضافة: 100.00٪ ، السعر العائم: 100.00٪

العديد من أعمدة القيمة:
Exterior1st: AsbShng ، AsphShn ، BrkComm ، BrkFace ، CBlock ، CemntBd ، HdBoard ، ImStucc ، MetalSd ، الخشب الرقائقي ، الحجر ، الجص ، فينيل إس دي ، Wd Sdng ، WdShing
Exterior2nd: AsbShng ، AsphShn ، Brk Cmn ، BrkFace ، CBlock ، CmentBd ، HdBoard ، ImStucc ، MetalSd ، أخرى ، الخشب الرقائقي ، الحجر ، الجص ، VinylSd ، Wd Sdng ، Wd Shng
الجوار: Blmngtn ، Blueste ، BrDale ، BrkSide ، ClearCr ، CollgCr ، Crawfor ، Edwards ، Gilbert ، IDOTRR ، MeadowV ، Mitchel ، NAmes ، NoRidge ، NPkVill ، NridgHt ، NWAmes ، OldTown ، Sawyer ، Sawyer ، Somerst ، فينكر

يطفو غير قابلة للتحليل
GarageYrBlt: NA
LotFrontage: NA
MasVnrArea: NA

يتراوح تعويم:
BsmtHalfBath: 0 ... 2
HalfBath: 0 ... 2
BsmtFullBath: 0 ... 3
المواقد: 0 ... 3
FullBath: 0 ... 3
KitchenAbvGr: 0 ... 3
كراج السيارات: 0 ... 4
YrSold: 2006 ... 2010
BedroomAbvGr: 0 ... 8
PoolArea: 0 ... 738
التقييم العام: 1 ... 9
التقييم العام: 1 ... 10
موسولد: 1 ... 12
TotRmsAbvGrd: 2 ... 14
MSSubClass: 20 ... 190
3SsNPorch: 0 ... 508
MiscVal: 0 ... 15500
LowQualFinSF: 0 ... 572
YearRemodAdd: 1950 ... 2010
ScreenPorch: 0 ... 480
GarageYrBlt: 1900 ... 2010
لوت فرونتاج: 21 ... 313
سنة البناء: 1872 ... 2010
EnclosedPorch: 0 ... 552
BsmtFinSF2: 0 ... 1474
OpenPorchSF: 0 ... 547
WoodDeckSF: 0 ... 857
MasVnrArea: 0 ... 1600
المركز الثاني: 0 ... 2065
المرآب: 0 ... 1418
BsmtFinSF1: 0 ... 5644
سعر البيع: 34،900 ... 755،000
TotalBsmtSF: 0 ... 6110
1stFlrSF: 334 ... 4692
BsmtUnfSF: 0 ... 2336
GrLivArea: 334 ... 5642
حجم العقد: 1300 ... 215245


مع وضع ذلك في الاعتبار ، قمت ببناء ValueNormalizer التالية ، والتي تأخذ بعض المعلومات حول القيم داخل العمود ، وتقوم بإرجاع دالة ، والتي تحول القيمة ( سلسلة ) إلى متجه ميزة رقمية للشبكة العصبية ( double [] ):

ValueNormalizer
 static Func<string, double[]> ValueNormalizer( double floats, IEnumerable<string> values) { if (floats > 0.01) { double max = values.AsDouble().Max().Value; return s => new[] { double.TryParse(s, out double v) ? v / max : -1 }; } else { string[] domain = values.Distinct().OrderBy(v => v).ToArray(); return s => new double[domain.Length+1] .Set(Array.IndexOf(domain, s)+1, 1); } } 


لقد قمنا الآن بتحويل البيانات إلى تنسيق مناسب للشبكة العصبية. لقد حان الوقت لبناء واحدة.

بناء شبكة عصبية


اعتبارًا من اليوم ، ستحتاج إلى استخدام جهاز Windows لذلك.

إذا كان لديك Python و TensorFlow 1.1x مثبتان بالفعل ، فكل ما تحتاجه هو

 <PackageReference Include="Gradient" Version="0.1.10-tech-preview3" /> 

في ملفك .csproj الحديث. خلاف ذلك ، راجع دليل التدرج اللوني للقيام بالإعداد الأولي.

بمجرد تشغيل الحزمة ، يمكننا إنشاء أول شبكة عميقة ضحلة.

 using tensorflow; using tensorflow.keras; using tensorflow.keras.layers; using tensorflow.train; ... var model = new Sequential(new Layer[] { new Dense(units: 16, activation: tf.nn.relu_fn), new Dropout(rate: 0.1), new Dense(units: 10, activation: tf.nn.relu_fn), new Dense(units: 1, activation: tf.nn.relu_fn), }); model.compile(optimizer: new AdamOptimizer(), loss: "mean_squared_error"); 

سيؤدي ذلك إلى إنشاء شبكة عصبية غير مدربة مع 3 طبقات من الخلايا العصبية ، وطبقة من التسرب ، تساعد على منع التحليق الزائد.

tf.nn.relu_fn
tf.nn.relu_fn هي وظيفة التنشيط للخلايا العصبية لدينا. من المعروف أن ReLU تعمل بشكل جيد في الشبكات العميقة ، لأنها تحل مشكلة التدرج اللانهائي : مشتقات وظائف التنشيط غير الخطية الأصلية تميل إلى أن تصبح صغيرة جدًا عندما ينتشر الخطأ مرة أخرى من طبقة الإخراج في الشبكات العميقة. وهذا يعني أن الطبقات الأقرب إلى المدخلات ستضبط قليلاً فقط ، مما يبطئ من تدريب الشبكات العميقة بشكل كبير.

التسرب
التسرب هو عبارة عن طبقة وظيفة خاصة في الشبكات العصبية ، والتي لا تحتوي في الواقع على خلايا عصبية على هذا النحو. بدلاً من ذلك ، تعمل عن طريق أخذ كل إدخال فردي ، واستبداله عشوائيًا بـ 0 في الإخراج الذاتي (وإلا فإنه يمرر القيمة الأصلية فقط). من خلال القيام بذلك ، فإنه يساعد على منع التحايل إلى ميزات أقل صلة في مجموعة بيانات صغيرة. على سبيل المثال ، إذا لم نقم بإزالة عمود المعرّف ، فمن المحتمل أن تكون الشبكة قد حفظت تخطيط <Id> -> <SalePrice> بالضبط ، مما يمنحنا دقة بنسبة 100 ٪ في مجموعة التدريب ، ولكن الأرقام غير ذات الصلة تمامًا على أي بيانات أخرى.

لماذا نحتاج التسرب؟ تحتوي بيانات التدريب الخاصة بنا على ما يقرب من 1500 نموذج فقط ، وهذه الشبكة العصبية الصغيرة التي أنشأناها لديها أكثر من 1800 أوزان قابلة للضبط. إذا كان سيكون متعدد الحدود بسيطًا ، فقد يتطابق مع دالة السعر التي نحاول تقريبها تمامًا. ولكن بعد ذلك سيكون له قيم هائلة على أي مدخلات خارج مجموعة التدريب الأصلية.

تغذية البيانات


تتوقع TensorFlow بياناتها إما في صفائف NumPy ، أو التنسورات الموجودة. أقوم بتحويل DataRows إلى صفائف NumPy:

 using numpy; ... const string predict = "SalePrice"; ndarray GetInputs(IEnumerable<DataRow> rowSeq) { return np.array(rowSeq.Select(row => np.array( columnTypes .Where(c => c.column.ColumnName != predict) .SelectMany(column => column.normalizer( row.Table.Columns.Contains(column.column.ColumnName) ? (string)row[column.column.ColumnName] : "-1")) .ToArray())) .ToArray() ); } var predictColumn = columnTypes.Single(c => c.column.ColumnName == predict); ndarray trainOutputs = np.array(predictColumn.trainValues .AsDouble() .Select(v => v ?? -1) .ToArray()); ndarray trainInputs = GetInputs(trainRows); 

في التعليمة البرمجية أعلاه ، نقوم بتحويل كل DataRow إلى ndarray عن طريق أخذ كل خلية فيه ، وتطبيق ValueNormalizer المطابق لعموده . ثم وضعنا جميع الصفوف في ndarray آخر ، والحصول على مجموعة من الصفائف.

ليس هناك حاجة إلى مثل هذا التحويل للمخرجات ، حيث نقوم فقط بتحويل قيم القطار إلى ndarray آخر.

الوقت لننكب التدرج


باستخدام هذا الإعداد ، كل ما نحتاج إلى القيام به لتدريب شبكتنا هو استدعاء وظيفة النموذج المناسبة :

 model.fit(trainInputs, trainOutputs, epochs: 2000, validation_split: 0.075, verbose: 2); 

ستخصص هذه المكالمة فعليًا آخر 7.5٪ من مجموعة التدريب للتحقق من صحتها ، ثم كرر مرات 2000 التالية:

  1. تقسيم بقية trainInputs إلى دفعات
  2. تغذية هذه الدفعات واحدا تلو الآخر في الشبكة العصبية
  3. حساب خطأ باستخدام وظيفة الخسارة التي حددناها أعلاه
  4. backpropagate الخطأ من خلال التدرجات من اتصالات الخلايا العصبية الفردية ، وضبط الأوزان

أثناء التدريب ، سينتج خطأ الشبكة على البيانات التي خصصتها للتحقق من الصحة كـ val_loss والخطأ في بيانات التدريب نفسها كخسارة فقط. بشكل عام ، إذا أصبحت val_loss أكبر بكثير من الخسارة ، فهذا يعني أن الشبكة بدأت في التحليق. سأتناول ذلك بمزيد من التفصيل في المقالات التالية.

إذا قمت بكل شيء بشكل صحيح ، فيجب أن يكون الجذر التربيعي لإحدى خسائرك هو 20000.



تقديم


لن أتحدث كثيرًا عن إنشاء الملف لإرساله هنا. رمز حساب المخرجات بسيط:

 const string SubmissionInputFile = "test.csv"; DataTable submissionData = LoadData(SubmissionInputFile); var submissionRows = submissionData.Rows.Cast<DataRow>(); ndarray submissionInputs = GetInputs(submissionRows); ndarray sumissionOutputs = model.predict(submissionInputs); 

الذي يستخدم في الغالب وظائف ، والتي تم تحديدها في وقت سابق.

بعد ذلك ، تحتاج إلى كتابتها في ملف بتنسيق .csv ، والذي هو ببساطة قائمة من المعرفات ، أزواج توقع القيمة.

عندما ترسل نتيجتك ، يجب أن تحصل على درجة بترتيب 0.17 ، والذي سيكون في مكان ما في الربع الأخير من جدول المتصدرين العامين. ولكن مهلا ، إذا كانت بسيطة مثل شبكة مكونة من 3 طبقات تضم 27 خلية عصبية ، فلن يحصل علماء البيانات المزعجون على تعويضات إجمالية قدرها 300 ألف دولار في السنة من الشركات الأمريكية الكبرى

اختتام


شفرة المصدر الكاملة لهذا الإدخال (مع جميع المساعدين ، وبعض الأجزاء التي علق عليها من الاستكشاف والتجارب السابقة) هي حوالي 200 سطر في PasteBin .

في المقالة التالية ، سترى عباقرة بلدي يحاولون الوصول إلى أفضل 50٪ من قائمة المتصدرين العامة. ستكون مغامرة هواة للمغامرين ، معركة مع Windmill of Overfitting باستخدام الأداة الوحيدة التي يمتلكها المتجول - نموذج أكبر (على سبيل المثال ، NN عميق ، تذكر ، لا توجد هندسة للميزات اليدوية!). سيكون أقل من تعليمي الترميز ، وأكثر من السعي الفكر مع الرياضيات المحتال حقا وخاتمة غريبة. ترقبوا!

الروابط


كاغل
أسعار المنازل المنافسة على Kaggle
TensorFlow الانحدار تعليمي
TensorFlow الصفحة الرئيسية
مرجع TensorFlow API
التدرج (تجانس TensorFlow)

Source: https://habr.com/ru/post/ar437174/


All Articles