خادم ويب التعلم الآلي "VKF Solver"

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







هذا هو نهج محلي لتعلم الآلة ، يسمى طريقة VKF للتعلم الآلي على أساس نظرية الشبكة. يتم شرح تاريخ حدوثه واختيار الاسم في نهاية هذه المقالة.



1. وصف الطريقة



في البداية ، تم إنشاء النظام بأكمله من قبل المؤلف في C ++ كتطبيق وحدة تحكم ، ثم تم توصيله بقاعدة البيانات تحت MariaDB DBMS (باستخدام مكتبة mariadb ++) ، ثم تم تحويله إلى مكتبة Python (باستخدام حزمة pybind11).

تم اختيار العديد من المصفوفات كبيانات اختبار لاختبار خوارزميات التعلم الآلي من مستودع جامعة كاليفورنيا في ايرفين.



على مجموعة الفطر ، التي تحتوي على أوصاف 8124 فطر في أمريكا الشمالية ، أظهر النظام نتائج 100٪. بتعبير أدق ، قسم مولد الأرقام العشوائية البيانات الأولية إلى مجموعة تدريب (2088 فطر صالح للأكل و 1944 سامة) ومجموعة اختبار (2120 صالحة للأكل و 1972 سامة). بعد حساب حوالي 100 فرضية حول أسباب قابلية الطعام ، تم توقع جميع حالات الاختبار بشكل صحيح. نظرًا لأن الخوارزمية تستخدم سلسلة ماركوف المقترنة ، يمكن أن يختلف عدد كافٍ من الفرضيات. في كثير من الأحيان اتضح أنه كافٍ لإنشاء 50 فرضية عشوائية. لاحظ أنه عند توليد أسباب السمية ، يتم تجميع عدد الفرضيات المطلوبة حول 120 ، ومع ذلك ، يتم توقع جميع حالات الاختبار بشكل صحيح في هذه الحالة. Kaggle.com لديها مسابقة تصنيف الفطرحيث حقق عدد غير قليل من المؤلفين دقة بنسبة 100٪. لكن معظم الحلول هي الشبكات العصبية. يسمح نهجنا لمنتقي الفطر أن يتعلم فقط حوالي 50 قاعدة. نظرًا لأن معظم العلامات غير ذات صلة ، ستكون كل فرضية أيضًا مقترنة بعدد صغير من قيم العلامات الأساسية ، مما يجعلها سهلة التذكر. بعد ذلك ، يمكن لمنتقي الفطر الذهاب إلى الفطر دون خوف من أخذ مقعد الضفدع أو فقدان فطر صالح للأكل.



في ما يلي مثال على إحدى الفرضيات التي يمكن على أساسها اعتبار الفطر صالحًا للأكل:

[('gill_attachment'، 'free')، ('gill_spacing'، 'close')، ('' gill_size '،' wide ')، (' stalk_shape '،' تكبير ')، (' stalk_surface_below_ring '،' scaly ')، (' veil_type '،' جزئي ')، (' veil_color '،' white ')، ('ring_number '،' one ')، (' ring_type '،' pendant ')]



أود أن ألفت انتباهك إلى حقيقة أن 9 فقط من بين 22 علامة تظهر في القائمة ، حيث لا يتم ملاحظة علامات التشابه الـ 13 المتبقية في الفطر الصالح للأكل الذي أدى إلى هذا السبب.



مجموعة أخرى كانت SPECT Hearts. هناك ، وصلت دقة التنبؤ بحالات الاختبار إلى 86.1٪ ، والتي اتضح أنها أعلى قليلاً من النتائج (84٪) لنظام التعلم الآلي CLIP3 ، استنادًا إلى تغطية أمثلة التدريب باستخدام البرمجة الصحيحة ، التي يستخدمها مؤلفو الصفيف. أعتقد أنه نظرًا لهيكل وصف التصوير المقطعي للقلب ، الذي تم ترميزه مسبقًا هناك بعلامات ثنائية ، فمن غير الممكن تحسين جودة التنبؤ بشكل كبير.



توصل المؤلف مؤخرًا إلى (وتم تنفيذه في البرنامج) امتدادًا لطريقته في معالجة البيانات الموصوفة بميزات (عددية) مستمرة. في بعض الجوانب ، يشبه نهجها نظام C4.5 لتعلم أشجار القرار. تم اختبار هذا الخيار على مصفوفة جودة النبيذ. تصف هذه المجموعة جودة النبيذ البرتغالي. النتائج مشجعة: إذا أخذنا النبيذ الأحمر عالي الجودة ، فإن الفرضيات تفسر تمامًا درجاتها العالية.



2. اختيار المنصة



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



ومع ذلك ، قررت أن أصف نسختي الشخصية هنا (باستخدام حزم aiohttp و aiojobs و aiomysql). لا يتم استخدام وحدة ذاكرة التخزين المؤقت بسبب مشاكل أمنية معروفة.



هناك مزايا عديدة للخيار المقترح:



  1. إنه غير متزامن بسبب استخدام aiohttp ؛
  2. أنه يسمح Templating Jinja2؛
  3. يعمل مع مجموعة من الاتصالات لقاعدة البيانات عبر aiomysql ؛
  4. تمكن من إطلاق العمليات الحسابية المستقلة عبر aiojobs.aiohttp.spawn.


دعونا نشير أيضًا إلى العيوب الواضحة (مقارنة بجانجو):



  1. لا يوجد تخطيط ارتباط الكائنات (ORM) ؛
  2. من الصعب تنظيم استخدام خادم وكيل Nginx ؛
  3. لغة قالب جانغو (DTL).


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



لحساب العديد من المستخدمين والتجارب والمراحل المختلفة التي يتواجدون فيها ، توجد قاعدة بيانات للخدمة (vkf) مع جدولين (مستخدمين ، تجارب). إذا قام جدول المستخدم بتخزين تسجيل الدخول وكلمة المرور لجميع المستخدمين المسجلين ، فإن التجارب ، بالإضافة إلى أسماء الجداول المساعدة والجداول الرئيسية لكل تجربة ، تبقي حالة هذه الجداول ممتلئة. لقد تخلصنا من aiohttp_session لأننا ما زلنا بحاجة إلى استخدام وكيل Nginx لحماية البيانات المهمة.



هنا هيكل جدول التجارب:



  • id int (11) ليس مفتاحًا أساسيًا فارغًا
  • expName varchar (255) ليس فارغًا
  • التشفير varchar (255)
  • جود انكودر مينينت (1)
  • المشابك varchar (255)
  • جودلاتيس ميني مينت (1)
  • فاركار معقدة (255)
  • جود كومبليكس مينينت (1)
  • verges varchar (255)
  • goodVerges tinyint (1)
  • مجموع int (11)
  • القطارات varchar (255) ليست فارغة
  • جودترينس مينينت (1)
  • tests varchar(255)
  • goodTests tinyint(1)
  • hypotheses varchar(255) NOT NULL
  • goodHypotheses tinyint(1)
  • type varchar(255) NOT NULL


وتجدر الإشارة إلى أن هناك بعض تسلسل إعداد البيانات لتجارب VKF ، والتي للأسف تختلف بشكل جذري عن الحالات المنفصلة والمستمرة. تجمع حالة السمات المختلطة بين متطلبات كلا النوعين.



منفصل: => goodLattices (شبه تلقائي)

منفصل: goodLattices => goodEncoder (تلقائي)

منفصل: goodEncoder => goodTrains (شبه تلقائي)

منفصل: goodEncoder، goodTrains => goodHypotheses

منفصل: goodEncoder => goodTests (شبه تلقائي)

منفصل goodEncoder، goodHypotheses => (تلقائي)

مستمر: => goodVerges (يدوي)

مستمر: goodVerges => goodTrains (يدوي)

مستمر: goodTrains => goodComplex (تلقائي)

مستمر: goodComplex، goodTrains => goodHypotheses (تلقائي)

مستمر: goodVerges => goodTests (يدوي)

مستمر: goodTests، goodComplex، goodHypotheses => (تلقائي)



مكتبة التعلم الآلي نفسها تسمى vkf.cpython -36m-x86_64-linux-gnu.so ضمن Linux أو vkf.cp36-win32.pyd ضمن Windows. (36 هي نسخة Python التي تم إنشاء هذه المكتبة لها).



المصطلح "تلقائي" يعني تشغيل هذه المكتبة ، يعني "شبه تلقائي" تشغيل المكتبة المساعدة vkfencoder.cpython-36m-x86_64-linux-gnu.so. أخيرًا ، الوضع "اليدوي" هو استدعاء للبرامج التي تعالج بيانات تجربة معينة بشكل خاص ويتم نقلها الآن إلى مكتبة vkfencoder.



3. تفاصيل التنفيذ



عند إنشاء خادم ويب ، نستخدم نهج "العرض / الطراز / التحكم"



. يوجد رمز Python في 5 ملفات:



  1. app.py - ملف تشغيل التطبيق
  2. control.py - ملف مع إجراءات للعمل مع VKF حلالا
  3. models.py - ملف مع فئات لمعالجة البيانات والعمل مع قاعدة البيانات
  4. settings.py - ملف يحتوي على إعدادات التطبيق
  5. views.py - ملف مع تصور ومعالجة الطرق (المسارات).


يحتوي ملف app.py على النموذج القياسي:



#! /usr/bin/env python
import asyncio
import jinja2
import aiohttp_jinja2

from settings import SITE_HOST as siteHost
from settings import SITE_PORT as sitePort

from aiohttp import web
from aiojobs.aiohttp import setup

from views import routes

async def init(loop):
    app = web.Application(loop=loop)
    # install aiojobs.aiohttp
    setup(app)
    # install jinja2 templates
    aiohttp_jinja2.setup(app, 
        loader=jinja2.FileSystemLoader('./template'))
    # add routes from api/views.py
    app.router.add_routes(routes)
    return app

loop = asyncio.get_event_loop()
try:
    app = loop.run_until_complete(init(loop))
    web.run_app(app, host=siteHost, port=sitePort)
except:
    loop.stop()


لا أعتقد أن هناك أي شيء لشرحه هنا. الملف التالي بترتيب التضمين في المشروع هو views.py:



import aiohttp_jinja2
from aiohttp import web#, WSMsgType
from aiojobs.aiohttp import spawn#, get_scheduler
from models import User
from models import Expert
from models import Experiment
from models import Solver
from models import Predictor

routes = web.RouteTableDef()

@routes.view(r'/tests/{name}', name='test-name')
class Predict(web.View):
    @aiohttp_jinja2.template('tests.html')
    async def get(self):
        return {'explanation': 'Please, confirm prediction!'}

    async def post(self):
        data = await self.request.post()
        db_name = self.request.match_info['name']
        analogy = Predictor(db_name, data)
        await analogy.load_data()
        job = await spawn(self.request, analogy.make_prediction())
        return await job.wait()

@routes.view(r'/vkf/{name}', name='vkf-name')
class Generate(web.View):
    #@aiohttp_jinja2.template('vkf.html')
    async def get(self):
        db_name = self.request.match_info['name']
        solver = Solver(db_name)
        await solver.load_data()
        context = { 'dbname': str(solver.dbname),
                    'encoder': str(solver.encoder),
                    'lattices': str(solver.lattices),
                    'good_lattices': bool(solver.lattices),
                    'verges': str(solver.verges),
                    'good_verges': bool(solver.good_verges),
                    'complex': str(solver.complex),
                    'good_complex': bool(solver.good_complex),
                    'trains': str(solver.trains),
                    'good_trains': bool(solver.good_trains),
                    'hypotheses': str(solver.hypotheses),
                    'type': str(solver.type)
            }
        response = aiohttp_jinja2.render_template('vkf.html', 
            self.request, context)
        return response
            
    async def post(self):
        data = await self.request.post()
        step = data.get('value')
        db_name = self.request.match_info['name']
        if step is 'init':
            location = self.request.app.router['experiment-name'].url_for(
                name=db_name)
            raise web.HTTPFound(location=location)
        solver = Solver(db_name)
        await solver.load_data()
        if step is 'populate':
            job = await spawn(self.request, solver.create_tables())
            return await job.wait()                
        if step is 'compute':
            job = await spawn(self.request, solver.compute_tables())
            return await job.wait()                
        if step is 'generate':
            hypotheses_total = data.get('hypotheses_total')
            threads_total = data.get('threads_total')
            job = await spawn(self.request, solver.make_induction(
                hypotheses_total, threads_total))
            return await job.wait()                

@routes.view(r'/experiment/{name}', name='experiment-name')
class Prepare(web.View):
    @aiohttp_jinja2.template('expert.html')
    async def get(self):
        return {'explanation': 'Please, enter your data'}

    async def post(self):
        data = await self.request.post()
        db_name = self.request.match_info['name']
        experiment = Experiment(db_name, data)
        job = await spawn(self.request, experiment.create_experiment())
        return await job.wait()


لقد قمت بتقصير هذا الملف من أجل هذه الملاحظة ، مع استبعاد الفئات التي تخدم مسارات المنفعة:



  1. Auth '/' . , SignIn, '/signin'. , '/user/{name}'.
  2. SignIn '/signin' .
  3. Select '/user/{name}' , . '/vkf/{name}' '/experiment/{name}' ( ).


تتعامل الفصول المتبقية مع المسارات المسؤولة عن خطوات تعلُم الآلة:



  1. يعالج الفصل Prepare المسارات '/ تجربة / {name}' ويجمع أسماء جداول الخدمة والمعلمات الرقمية المطلوبة لتشغيل إجراءات طريقة VKF. بعد حفظ هذه المعلومات في قاعدة البيانات ، تتم إعادة توجيه المستخدم إلى المسار "/ vkf / {name}".
  2. تقوم الفئة Generate بمعالجة المسارات "/ vkf / {name}" وتبدأ المراحل المختلفة لإجراء الحث لطريقة VKF ، اعتمادًا على البيانات التي أعدها الخبير.
  3. تقوم فئة التنبؤ بمعالجة المسارات "/ اختبارات / {name}" وتبدأ إجراء طريقة التنبؤ VKF عن طريق القياس.


لنقل عدد كبير من المعلمات إلى نموذج vkf.html ، يتم استخدام البناء من aiohttp_jinja2



response = aiohttp_jinja2.render_template('vkf.html', self.request, context)
return response




لاحظ أيضًا استخدام نداء التفرخ من حزمة aiojobs.aiohttp:



job = await spawn(self.request, 
    solver.make_induction(hypotheses_total, threads_total))
return await job.wait()


يعد ذلك ضروريًا لاستدعاء الإجراءات المشتركة بأمان من الفئات المحددة في ملف models.py الذي يعالج بيانات المستخدم والتجارب المخزنة في قاعدة البيانات تحت سيطرة MariaDB DBMS:



import aiomysql
from aiohttp import web

from settings import AUX_NAME as auxName
from settings import AUTH_TABLE as authTable
from settings import AUX_TABLE as auxTable
from settings import SECRET_KEY as secretKey
from settings import DB_HOST as dbHost

from control import createAuxTables
from control import createMainTables
from control import computeAuxTables
from control import induction
from control import prediction

class Experiment():
    def __init__(self, dbName, data, **kw):
        self.encoder = data.get('encoder_table')
        self.lattices = data.get('lattices_table')
        self.complex = data.get('complex_table')
        self.verges = data.get('verges_table')
        self.verges_total = data.get('verges_total')
        self.trains = data.get('training_table')
        self.tests = data.get('tests_table')
        self.hypotheses = data.get('hypotheses_table')
        self.type = data.get('type')
        self.auxname = auxName
        self.auxtable = auxTable
        self.dbhost = dbHost
        self.secret = secretKey
        self.dbname = dbName

    async def create_db(self, pool):
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                await cur.execute("CREATE DATABASE IF NOT EXISTS " +
                    str(self.dbname)) 
                await conn.commit() 
        await createAuxTables(self)
 
    async def register_experiment(self, pool):
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                sql = "INSERT INTO " + str(self.auxname) + "." + 
                    str(self.auxtable)
                sql += " VALUES(NULL, '" 
                sql += str(self.dbname) 
                sql += "', '" 
                sql += str(self.encoder) 
                sql += "', 0, '" #goodEncoder
                sql += str(self.lattices) 
                sql += "', 0, '" #goodLattices
                sql += str(self.complex) 
                sql += "', 0, '" #goodComplex 
                sql += str(self.verges_total) 
                sql += "', 0, " #goodVerges
                sql += str(self.verges_total) 
                sql += ", '" 
                sql += str(self.trains) 
                sql += "', 0, '" #goodTrains 
                sql += str(self.tests) 
                sql += "', 0, '" #goodTests 
                sql += str(self.hypotheses) 
                sql += "', 0, '" #goodHypotheses 
                sql += str(self.type)
                sql += "')"
                await cur.execute(sql)
                await conn.commit() 

    async def create_experiment(self, **kw):
        pool = await aiomysql.create_pool(host=self.dbhost, 
            user='root', password=self.secret)
        task1 = self.create_db(pool=pool)
        task2 = self.register_experiment(pool=pool)
        tasks = [asyncio.ensure_future(task1), 
            asyncio.ensure_future(task2)]
        await asyncio.gather(*tasks)
        pool.close()
        await pool.wait_closed()
        raise web.HTTPFound(location='/vkf/' + self.dbname)        

class Solver():
    def __init__(self, dbName, **kw):
        self.auxname = auxName
        self.auxtable = auxTable
        self.dbhost = dbHost
        self.dbname = dbName
        self.secret = secretKey

    async def load_data(self, **kw):    
        pool = await aiomysql.create_pool(host=dbHost, 
            user='root', password=secretKey, db=auxName)
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                sql = "SELECT * FROM "
                sql += str(auxTable)
                sql += " WHERE  expName='"
                sql += str(self.dbname)
                sql += "'"
                await cur.execute(sql)
                row = cur.fetchone()
                await cur.close()
        pool.close()
        await pool.wait_closed()
        self.encoder = str(row.result()[2])
        self.good_encoder = bool(row.result()[3])
        self.lattices = str(row.result()[4])
        self.good_lattices = bool(row.result()[5])
        self.complex = str(row.result()[6])
        self.good_complex = bool(row.result()[7])
        self.verges = str(row.result()[8])
        self.good_verges = bool(row.result()[9])
        self.verges_total = int(row.result()[10])
        self.trains = str(row.result()[11])
        self.good_trains = bool(row.result()[12])
        self.hypotheses = str(row.result()[15])
        self.good_hypotheses = bool(row.result()[16])
        self.type = str(row.result()[17])

    async def create_tables(self, **kw):
        await createMainTables(self)
        pool = await aiomysql.create_pool(host=self.dbhost, user='root', 
            password=self.secret, db=self.auxname)
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                sql = "UPDATE "
                sql += str(self.auxtable)
                sql += " SET encoderStatus=1 WHERE dbname='"
                sql += str(self.dbname)
                sql += "'"
                await cur.execute(sql) 
                await conn.commit() 
                await cur.close()
        pool.close()
        await pool.wait_closed()
        raise web.HTTPFound(location='/vkf/' + self.dbname)        

    async def compute_tables(self, **kw):
        await computeAuxTables(self)
        pool = await aiomysql.create_pool(host=self.dbhost, user='root', 
            password=self.secret, db=self.auxname)
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                sql = "UPDATE "
                sql += str(self.auxtable)
                sql += " SET complexStatus=1 WHERE dbname='"
                sql += str(self.dbname)
                sql += "'"
                await cur.execute(sql) 
                await conn.commit() 
                await cur.close()
        pool.close()
        await pool.wait_closed()
        raise web.HTTPFound(location='/vkf/' + self.dbname)        

    async def make_induction(self, hypotheses_total, threads_total, **kw):
        await induction(self, hypotheses_total, threads_total)
        pool = await aiomysql.create_pool(host=self.dbhost, user='root', 
            password=self.secret, db=self.auxname)
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                sql = "UPDATE "
                sql += str(self.auxtable)
                sql += " SET hypothesesStatus=1 WHERE dbname='"
                sql += str(self.dbname)
                sql += "'"
                await cur.execute(sql) 
                await conn.commit() 
                await cur.close()
        pool.close()
        await pool.wait_closed()
        raise web.HTTPFound(location='/tests/' + self.dbname)        

class Predictor():
    def __init__(self, dbName, data, **kw):
        self.auxname = auxName
        self.auxtable = auxTable
        self.dbhost = dbHost
        self.dbname = dbName
        self.secret = secretKey
        self.plus = 0
        self.minus = 0

    async def load_data(self, **kw):    
        pool = await aiomysql.create_pool(host=dbHost, user='root', 
            password=secretKey, db=auxName)
        async with pool.acquire() as conn:
            async with conn.cursor() as cur:
                sql = "SELECT * FROM "
                sql += str(auxTable)
                sql += " WHERE dbname='"
                sql += str(self.dbname)
                sql += "'"
                await cur.execute(sql) 
                row = cur.fetchone()
                await cur.close()
        pool.close()
        await pool.wait_closed()
        self.encoder = str(row.result()[2])
        self.good_encoder = bool(row.result()[3])
        self.complex = str(row.result()[6])
        self.good_complex = bool(row.result()[7])
        self.verges = str(row.result()[8])
        self.trains = str(row.result()[11])
        self.tests = str(row.result()[13])
        self.good_tests = bool(row.result()[14])
        self.hypotheses = str(row.result()[15])
        self.good_hypotheses = bool(row.result()[16])
        self.type = str(row.result()[17])

    async def make_prediction(self, **kw):
        if self.good_tests and self.good_hypotheses:
            await induction(self, 0, 1)
            await prediction(self)
            message_body = str(self.plus)
            message_body += " correct positive cases. "
            message_body += str(self.minus)
            message_body += " correct negative cases."
            raise web.HTTPException(body=message_body)
        else:
            raise web.HTTPFound(location='/vkf/' + self.dbname)




مرة أخرى ، يتم إخفاء بعض فئات المساعد:



  1. تتوافق فئة المستخدم مع زائر الموقع. يسمح لك بالتسجيل وتسجيل الدخول كخبير.
  2. يسمح لك الفصل الخبير باختيار إحدى التجارب.


الفئات المتبقية تتوافق مع الإجراءات الرئيسية:



  1. تتيح لك فئة التجربة تحديد أسماء الجداول الرئيسية والمساعدات والمعلمات المطلوبة لإجراء تجارب ICF.
  2. إن فئة Solver مسؤولة عن التعميم الاستقرائي في طريقة ICF.
  3. إن فئة Predictor مسؤولة عن التنبؤات عن طريق القياس في طريقة CCF.


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



pool = await aiomysql.create_pool(host=self.dbhost, 
    user='root', password=self.secret)
task1 = self.create_db(pool=pool)
task2 = self.register_experiment(pool=pool)
tasks = [asyncio.ensure_future(task1), 
    asyncio.ensure_future(task2)]
await asyncio.gather(*tasks)
pool.close()
await pool.wait_closed()


عند القراءة من جدول ، يُرجع row = cur.fetchone () مستقبلاً ، لذا يُرجع row.result () سجل قاعدة بيانات يمكن استرداد قيم الحقول منه (على سبيل المثال ، يسترد str (row.result () [2]) اسم الجدول باستخدام ترميز قيم الميزات المنفصلة).




pool = await aiomysql.create_pool(host=dbHost, user='root', 
    password=secretKey, db=auxName)
async with pool.acquire() as conn:
    async with conn.cursor() as cur:
        await cur.execute(sql) 
        row = cur.fetchone()
        await cur.close()
pool.close()
await pool.wait_closed()
self.encoder = str(row.result()[2])


يتم استيراد معلمات النظام الرئيسية من ملف .env أو (إذا كانت مفقودة) من ملف settings.py.



from os.path import isfile
from envparse import env

if isfile('.env'):
    env.read_envfile('.env')

AUX_NAME = env.str('AUX_NAME', default='vkf')
AUTH_TABLE = env.str('AUTH_TABLE', default='users')
AUX_TABLE = env.str('AUX_TABLE', default='experiments')
DB_HOST = env.str('DB_HOST', default='127.0.0.1')
DB_HOST = env.str('DB_PORT', default=3306)
DEBUG = env.bool('DEBUG', default=False)
SECRET_KEY = env.str('SECRET_KEY', default='toor')
SITE_HOST = env.str('HOST', default='127.0.0.1')
SITE_PORT = env.int('PORT', default=8080)


من المهم ملاحظة أنه يجب تحديد localhost عن طريق عنوان IP ، وإلا سيحاول aiomysql الاتصال بقاعدة البيانات عبر مقبس Unix ، والذي قد لا يعمل ضمن Windows. أخيرًا ، قم بتشغيل الملف الأخير (control.py):



import os
import asyncio
import vkf

async def createAuxTables(db_data):
    if  db_data.type is not "discrete":
        await vkf.CAttributes(db_data.verges, db_data.dbname, 
            '127.0.0.1', 'root', db_data.secret)
    if  db_data.type is not "continuous":
        await vkf.DAttributes(db_data.encoder, db_data.dbname, 
            '127.0.0.1', 'root', db_data.secret)
        await vkf.Lattices(db_data.lattices, db_data.dbname, 
            '127.0.0.1', 'root', db_data.secret) 

async def createMainTables(db_data):
    if  db_data.type is "continuous":
        await vkf.CData(db_data.trains, db_data.verges, 
            db_data.dbname, '127.0.0.1', 'root', db_data.secret)
        await vkf.CData(db_data.tests, db_data.verges, 
            db_data.dbname, '127.0.0.1', 'root', db_data.secret)
    if  db_data.type is "discrete":
        await vkf.FCA(db_data.lattices, db_data.encoder, 
            db_data.dbname, '127.0.0.1', 'root', db_data.secret)
        await vkf.DData(db_data.trains, db_data.encoder, 
            db_data.dbname, '127.0.0.1', 'root', db_data.secret)
        await vkf.DData(db_data.tests, db_data.encoder, 
            db_data.dbname, '127.0.0.1', 'root', db_data.secret)
    if  db_data.type is "full":
        await vkf.FCA(db_data.lattices, db_data.encoder, 
            db_data.dbname, '127.0.0.1', 'root', db_data.secret)
        await vkf.FData(db_data.trains, db_data.encoder, db_data.verges, 
            db_data.dbname, '127.0.0.1', 'root', db_data.secret)
        await vkf.FData(db_data.tests, db_data.encoder, db_data.verges, 
            db_data.dbname,'127.0.0.1', 'root', db_data.secret)

async def computeAuxTables(db_data):
    if  db_data.type is not "discrete":
        async with vkf.Join(db_data.trains, db_data.dbname, '127.0.0.1', 
            'root', db_data.secret) as join:
            await join.compute_save(db_data.complex, db_data.dbname, 
                '127.0.0.1', 'root', db_data.secret)
        await vkf.Generator(db_data.complex, db_data.trains, db_data.verges, 
            db_data.dbname, db_data.dbname, db_data.verges_total, 1, 
            '127.0.0.1', 'root', db_data.secret)

async def induction(db_data, hypothesesNumber, threadsNumber):
    if  db_data.type is not "discrete":
        qualifier = await vkf.Qualifier(db_data.verges, 
            db_data.dbname, '127.0.0.1', 'root', db_data.secret)
        beget = await vkf.Beget(db_data.complex, db_data.dbname, 
            '127.0.0.1', 'root', db_data.secret)
    if  db_data.type is not "continuous":
        encoder = await vkf.Encoder(db_data.encoder, db_data.dbname, 
            '127.0.0.1', 'root', db_data.secret)
    async with vkf.Induction() as induction: 
        if  db_data.type is "continuous":
            await induction.load_continuous_hypotheses(qualifier, beget, 
                db_data.trains, db_data.hypotheses, db_data.dbname, 
                '127.0.0.1', 'root', db_data.secret)
        if  db_data.type is "discrete":
            await induction.load_discrete_hypotheses(encoder, 
                db_data.trains, db_data.hypotheses, db_data.dbname, 
                '127.0.0.1', 'root', db_data.secret)
        if  db_data.type is "full":
            await induction.load_full_hypotheses(encoder, qualifier, beget, 
                db_data.trains, db_data.hypotheses, db_data.dbname, 
                '127.0.0.1', 'root', db_data.secret)
        if hypothesesNumber > 0:
            await induction.add_hypotheses(hypothesesNumber, threadsNumber)
            if  db_data.type is "continuous":
                await induction.save_continuous_hypotheses(qualifier, 
                    db_data.hypotheses, db_data.dbname, '127.0.0.1', 'root', 
                    db_data.secret)
            if  db_data.type is "discrete":
                await induction.save_discrete_hypotheses(encoder, 
                    db_data.hypotheses, db_data.dbname, '127.0.0.1', 'root', 
                    db_data.secret)
            if  db_data.type is "full":
                await induction.save_full_hypotheses(encoder, qualifier, 
                    db_data.hypotheses, db_data.dbname, '127.0.0.1', 'root', 
                    db_data.secret)

async def prediction(db_data):
    if  db_data.type is not "discrete":
        qualifier = await vkf.Qualifier(db_data.verges, 
            db_data.dbname, '127.0.0.1', 'root', db_data.secret)
        beget = await vkf.Beget(db_data.complex, db_data.dbname, 
            '127.0.0.1', 'root', db_data.secret)
    if  db_data.type is not "continuous":
        encoder = await vkf.Encoder(db_data.encoder, 
            db_data.dbname, '127.0.0.1', 'root', db_data.secret)
    async with vkf.Induction() as induction: 
        if  db_data.type is "continuous":
            await induction.load_continuous_hypotheses(qualifier, beget, 
                db_data.trains, db_data.hypotheses, db_data.dbname, 
                '127.0.0.1', 'root', db_data.secret)
        if  db_data.type is "discrete":
            await induction.load_discrete_hypotheses(encoder, 
                db_data.trains, db_data.hypotheses, db_data.dbname, 
                '127.0.0.1', 'root', db_data.secret)
        if  db_data.type is "full":
            await induction.load_full_hypotheses(encoder, qualifier, beget, 
                db_data.trains, db_data.hypotheses, db_data.dbname, 
                '127.0.0.1', 'root', db_data.secret)
        if  db_data.type is "continuous":
            async with vkf.TestSample(qualifier, induction, beget, 
                db_data.tests, db_data.dbname, '127.0.0.1', 'root', 
                db_data.secret) as tests:
                #plus = await tests.correct_positive_cases()
                db_data.plus = await tests.correct_positive_cases()
                #minus = await tests.correct_negative_cases()
                db_data.minus = await tests.correct_negative_cases()
        if  db_data.type is "discrete":
            async with vkf.TestSample(encoder, induction, 
                db_data.tests, db_data.dbname, '127.0.0.1', 'root', 
                db_data.secret) as tests:
                #plus = await tests.correct_positive_cases()
                db_data.plus = await tests.correct_positive_cases()
                #minus = await tests.correct_negative_cases()
                db_data.minus = await tests.correct_negative_cases()
        if  db_data.type is "full":
            async with vkf.TestSample(encoder, qualifier, induction, 
                beget, db_data.tests, db_data.dbname, '127.0.0.1', 
                'root', db_data.secret) as tests:
                #plus = await tests.correct_positive_cases()
                db_data.plus = await tests.correct_positive_cases()
                #minus = await tests.correct_negative_cases()
                db_data.minus = await tests.correct_negative_cases()


لقد حفظت هذا الملف بالكامل ، حيث يمكنك هنا رؤية الأسماء وترتيب الاتصال والوسيطات الخاصة بإجراءات طريقة VKF من مكتبة vkf.cpython-36m-x86_64-linux-gnu.so. يمكن حذف جميع الوسائط بعد dbname ، حيث يتم تعيين القيم الافتراضية في مكتبة CPython بقيم قياسية.



4. التعليقات



توقعًا للمبرمجين المحترفين حول سبب ظهور منطق التحكم في تجربة VKF (من خلال العديد من ifs) ، وعدم إخفاؤه من خلال تعدد الأشكال إلى أنواع ، يجب أن تكون الإجابة على النحو التالي: للأسف ، لا تسمح الكتابة الديناميكية للغة Python بتغيير القرار حول نوع الكائن المستخدم في النظام ، أي أن هذا التسلسل لوحدات ifs المتداخلة سيحدث على أي حال. لذلك ، فضل المؤلف استخدام بنية صريحة (مثل C) لجعل المنطق شفافًا (وفعالًا) قدر الإمكان.



اسمحوا لي أن أعلق على المكونات المفقودة:



  1. vkfencoder.cpython-36m-x86_64-linux-gnu.so (web- , , ). vkfencoder.cpython-36m-x86_64-linux-gnu.so.
  2. - MariaDB ( DBeaver 7.1.1 Community, ). Django, ORM .


5.



شارك المؤلف في مهام استخراج البيانات لأكثر من 30 عامًا. بعد التخرج من كلية الميكانيكا والرياضيات في جامعة موسكو الحكومية Lomonosov ، تمت دعوته إلى مجموعة من الباحثين تحت قيادة دكتور العلوم التقنية ، الأستاذ. VK. الفنلندي (VINITI AN SSSR). منذ بداية الثمانينيات من القرن الماضي ، كان فيكتور كونستانتينوفيتش يستكشف المنطق المعقول وإضفاء الطابع الرسمي عليه من خلال المنطق متعدد القيم.



الأفكار الرئيسية التي اقترحها VK. الفنلندي ، يمكن اعتبار ما يلي:



  1. استخدام عملية تشابه ثنائي (في الأصل ، عملية التقاطع في الجبر البولي) ؛
  2. فكرة التخلص من التشابه الناتج عن مجموعة من أمثلة التدريب إذا كانت مدمجة في وصف مثال للعلامة المعاكسة (مثال مضاد) ؛
  3. فكرة التنبؤ بالممتلكات (المستهدفة) التي تم التحقيق فيها من الأمثلة الجديدة من خلال مراعاة الإيجابيات والسلبيات ؛
  4. فكرة التحقق من اكتمال مجموعة من الفرضيات من خلال إيجاد الأسباب (من بين أوجه التشابه المتولدة) لوجود / غياب خاصية مستهدفة في أمثلة التدريب.


وتجدر الإشارة إلى أن V.K. ينسب فين بعض أفكاره إلى مؤلفين أجانب. ربما فقط منطق الجدل يعتبره عن حق أنه اخترع بشكل مستقل. فكرة المحاسبة عن الأمثلة المضادة بواسطة VK. استعار فين ، حسب قوله ، من ك. بوبر. وأصول التحقق من اكتمال التعميم الاستقرائي تنتمي إليه (غامضة تمامًا ، في رأيي) أعمال عالم الرياضيات الأمريكي والمنطق C.S. بيرس. وهو يعتبر إنشاء فرضيات حول أسباب استخدام عملية التشابه يمكن استعارتها من أفكار الاقتصادي والفيلسوف والمنطق البريطاني D.S. مطحنة. لذلك ، ابتكر مجموعة من الأفكار التي حملها بعنوان "طريقة DSM" تكريماً لـ D.S. مطحنة.



غريب ، لكنه ظهر في أواخر السبعينات من القرن العشرين في أعمال الأستاذ. رودولف ويلي (FRG) ، قسم أكثر فائدة بكثير من النظرية الجبرية للشبكات "تحليل المفاهيم الرسمية" (AFP) لا يستخدم من قبل V.K. تحيات فين. في رأيي ، فإن السبب في ذلك هو الاسم المؤسف ، الذي يتسبب في الرفض ، مثل الشخص الذي تخرج لأول مرة من كلية الفلسفة ، ثم التيار الهندسي لكلية الميكانيكا والرياضيات في جامعة موسكو الحكومية.



كخليفة لعمل معلمه ، أطلق المؤلف على منهجه "طريقة VKF" تكريمًا له. ومع ذلك ، هناك فك تشفير آخر - طريقة تعلّم آلي اندماجي احتمالي قائمة على نظرية شبكية.



الآن مجموعة VK. تعمل الفنانة في مركز المعارض. أ. Dorodnicyn RAS FRC IU RAS وفي قسم الأنظمة الذكية في جامعة الدولة الروسية للعلوم الإنسانية.



يمكن العثور على مزيد من المعلومات حول رياضيات حلالا VKF في أطروحة المؤلف أو محاضرات الفيديو الخاصة به في جامعة ولاية أوليانوفسك (المؤلف ممتن لـ A. B. Verevkin و N. G. Baranets لتنظيم المحاضرات ومعالجة ملاحظاتهم).



يتم تخزين الحزمة الكاملة لملفات المصدر على Bitbucket .



ملفات المصدر (في C ++) لمكتبة vkf بصدد الموافقة على وضعها في savannah.nongnu.org. إذا كان الأمر كذلك ، فسيتم إضافة رابط تنزيل هنا.



أخيرًا ، ملاحظة أخيرة: بدأ المؤلف تعلم Python في 6 أبريل 2020. حتى ذلك الحين ، كانت اللغة الوحيدة التي قام ببرمجتها هي C ++. لكن هذا الظرف لا يعفيه من الاتهامات بأن المدونة قد تكون غير دقيقة.



المؤلف ممتن لـ Tatyana A. Volkovarobofreakللحصول على الدعم والاقتراحات البناءة والانتقادات ، مما سمح بتحسين العرض بشكل كبير (وحتى تحسين الرمز بشكل ملحوظ). ومع ذلك ، فإن المسؤولية عن الأخطاء والقرارات المتبقية المتخذة (حتى خلافا لنصيحتها) تقع على عاتق المؤلف فقط.



All Articles