Webserver für maschinelles Lernen "VKF-Solver"

In den Augen der Öffentlichkeit ist maschinelles Lernen stark mit verschiedenen Optionen für das Training neuronaler Netze verbunden. Wenn es sich anfangs um vollständig verbundene Netzwerke handelte, wurden sie durch Faltungsnetzwerke und wiederkehrende Netzwerke ersetzt. Jetzt sind sie zu vollständig exotischen Optionen wie GAN- und LTSM-Netzwerken geworden. Zusätzlich zu den zunehmenden Probenmengen, die für ihre Ausbildung benötigt werden, leiden sie immer noch unter der Unmöglichkeit zu erklären, warum diese oder jene Entscheidung getroffen wurde. Es gibt aber auch strukturelle Ansätze für maschinelles Lernen, von denen die Software-Implementierung in diesem Artikel beschrieben wird.







Dies ist ein inländischer Ansatz für maschinelles Lernen, der als VKF-Methode des maschinellen Lernens bezeichnet wird und auf der Gittertheorie basiert. Die Entstehungsgeschichte und die Wahl des Namens werden ganz am Ende dieses Artikels erläutert.



1. Beschreibung der Methode



Zunächst wurde das gesamte System vom Autor in C ++ als Konsolenanwendung erstellt, dann mit einer Datenbank unter der Kontrolle des MariaDB-DBMS (unter Verwendung der Mariadb ++ - Bibliothek) verbunden und dann in eine Python-Bibliothek (unter Verwendung des Pakets pybind11) umgewandelt.

Mehrere Arrays wurden als Testdaten zum Testen von Algorithmen für maschinelles Lernen aus dem Repository der University of California in Irvine ausgewählt.



Auf dem Pilzarray, das Beschreibungen von 8124 nordamerikanischen Pilzen enthielt, zeigte das System 100% Ergebnisse. Genauer gesagt wurden unter Verwendung eines Zufallszahlengenerators die Anfangsdaten in eine Trainingsprobe (2088 essbare und 1944 giftige Pilze) und eine Testprobe (2120 essbare und 1972 giftige Pilze) unterteilt. Nach der Berechnung von etwa 100 Hypothesen über die Ursachen der Essbarkeit wurden alle Testfälle korrekt vorhergesagt. Da der Algorithmus eine gepaarte Markov-Kette verwendet, kann eine ausreichende Anzahl von Hypothesen variieren. Sehr oft stellte sich heraus, dass es ausreichte, 50 zufällige Hypothesen zu erstellen. Beachten Sie, dass bei der Generierung der Toxizitätsursachen die Anzahl der erforderlichen Hypothesen um 120 gruppiert wird. In diesem Fall werden jedoch alle Testfälle korrekt vorhergesagt. Kaggle.com hat einen Pilz Klassifizierung Wettbewerbwo etliche Autoren 100% Genauigkeit erreicht haben. Die meisten Lösungen sind jedoch neuronale Netze. Unser Ansatz ermöglicht es dem Pilzsammler, nur etwa 50 Regeln zu lernen. Da die meisten Merkmale unbedeutend sind, ist jede Hypothese eine Verbindung einer kleinen Anzahl von Werten wesentlicher Merkmale, wodurch sie leicht zu merken sind. Danach kann der Pilzsammler Pilze fangen, ohne befürchten zu müssen, einen Giftpilz zu nehmen oder einen essbaren Pilz zu verpassen.



Hier ist ein Beispiel für eine der Hypothesen, auf deren Grundlage angenommen werden kann, dass der Pilz essbar ist:

[('gill_attachment', 'free'), ('gill_spacing', 'close'), ('gill_size', 'wide'), ('stalk_shape') ',' vergrößern '), (' stalk_surface_below_ring ',' scaly '), (' veil_type ',' partiell '), (' veil_color ',' white '), ('ring_number ',' one '), (' ring_type ',' pendant ')]



Ich mache Sie darauf aufmerksam, dass nur 9 von 22 Zeichen in der Liste aufgeführt sind, da die verbleibenden 13 Ähnlichkeitszeichen bei den Speisepilzen, die diesen Grund hervorgebracht haben, nicht beobachtet werden.



Ein weiteres Array war SPECT Hearts. Dort erreichte die Vorhersagegenauigkeit von Testfällen 86,1%, was sich als geringfügig höher als die Ergebnisse (84%) des maschinellen Lernsystems CLIP3 herausstellte, basierend auf der von den Autoren des Arrays verwendeten Trainingsbeispielabdeckung unter Verwendung von Ganzzahlprogrammierung. Ich glaube, dass es aufgrund der Struktur der Beschreibung der Tomogramme des Herzens, die bereits mit binären Zeichen vorcodiert sind, nicht möglich ist, die Prognosequalität signifikant zu verbessern.



Der Autor hat kürzlich eine Erweiterung seines Ansatzes zur Verarbeitung von Daten entwickelt (und in Software implementiert), die durch kontinuierliche (numerische) Merkmale beschrieben wird. In einigen Aspekten ähnelt sein Ansatz dem C4.5-System zum Lernen von Entscheidungsbäumen. Diese Variante wurde auf dem Wine Quality Array getestet. Dieses Array beschreibt die Qualität portugiesischer Weine. Die Ergebnisse sind ermutigend: Wenn wir hochwertige Rotweine nehmen, erklären die Hypothesen ihre hohen Punktzahlen vollständig.



2. Wahl der Plattform



Derzeit wird durch die Bemühungen der Studenten der Abteilung für Intelligente Systeme der Russischen Staatlichen Humanitären Universität eine Reihe von Webservern für verschiedene Arten von Aufgaben erstellt (unter Verwendung des Nginx + Gunicorn + Django-Bundles).



Ich habe mich jedoch entschlossen, meine persönliche Version hier zu beschreiben (mit einer Reihe von aiohttp, aiojobs und aiomysql). Das aiomcache-Modul wird aufgrund bekannter Sicherheitsprobleme nicht verwendet.



Die vorgeschlagene Option bietet mehrere Vorteile:



  1. es ist aufgrund der Verwendung von aiohttp asynchron;
  2. es erlaubt Jinja2 Templating;
  3. Es funktioniert mit einem Pool von Verbindungen zur Datenbank über aiomysql.
  4. Es ermöglicht den Start unabhängiger Rechenprozesse über aiojobs.aiohttp.spawn.


Wir weisen auf die offensichtlichen Nachteile hin (im Vergleich zu Django):



  1. keine objektrelationale Zuordnung (ORM);
  2. Es ist schwieriger, die Verwendung des Nginx-Proxyservers zu organisieren.
  3. Keine Django-Vorlagensprache (DTL).


Jede der beiden Optionen zielt auf unterschiedliche Strategien für die Arbeit mit einem Webserver ab. Die synchrone Strategie (in Django) zielt auf einen Einzelbenutzermodus ab, in dem ein Experte jeweils mit einer einzelnen Datenbank arbeitet. Obwohl die probabilistischen Verfahren der CCF-Methode bemerkenswert parallelisiert sind, wird theoretisch ein Fall nicht ausgeschlossen, in dem maschinelle Lernverfahren beträchtliche Zeit in Anspruch nehmen. Daher richtet sich die in diesem Hinweis beschriebene Option an mehrere Experten, von denen jeder gleichzeitig (in verschiedenen Browser-Registerkarten) mit verschiedenen Datenbanken arbeiten kann, die sich nicht nur in den Daten, sondern auch in der Art und Weise ihrer Darstellung unterscheiden (unterschiedliche Gitter auf den Werten diskreter Merkmale, unterschiedliche signifikante Regressionen und die Anzahl Schwellenwerte für kontinuierliche). Wenn Sie dann das VKF-Experiment auf einer Registerkarte starten, kann der Experte zu einer anderen wechseln.Wo wird ein Experiment mit verschiedenen Daten und / oder Parametern vorbereitet oder analysiert?



Um mehrere Benutzer, Experimente und die verschiedenen Phasen, in denen sie sich befinden, zu berücksichtigen, gibt es eine Servicedatenbank (vkf) mit zwei Tabellen (Benutzer, Experimente). Wenn in der Benutzertabelle die Anmeldung und das Kennwort aller registrierten Benutzer gespeichert sind, behalten Experimente zusätzlich zu den Namen der Hilfs- und Haupttabellen jedes Experiments den Status der Fülle dieser Tabellen bei. Wir haben aiohttp_session aufgegeben, da Sie zum Schutz kritischer Daten weiterhin den Nginx-Proxyserver verwenden müssen.



Hier ist die Struktur der Versuchstabelle:



  • id int (11) NICHT NULL PRIMARY KEY
  • expName varchar (255) NICHT NULL
  • Encoder Varchar (255)
  • goodEncoder tinyint (1)
  • Gitter varchar (255)
  • goodLattices tinyint (1)
  • komplexer Varchar (255)
  • goodComplex tinyint (1)
  • Rand Varchar (255)
  • goodVerges tinyint (1)
  • vergesTotal int (11)
  • Züge varchar (255) NICHT NULL
  • goodTrains tinyint (1)
  • tests varchar(255)
  • goodTests tinyint(1)
  • hypotheses varchar(255) NOT NULL
  • goodHypotheses tinyint(1)
  • type varchar(255) NOT NULL


Es ist zu beachten, dass es einige Sequenzen der Datenaufbereitung für VKF-Experimente gibt, die sich leider für diskrete und kontinuierliche Fälle radikal unterscheiden. Der Fall gemischter Attribute kombiniert Anforderungen beider Typen.



Diskret: => goodLattices (halbautomatisch)

wählbar: goodLattices => goodEncoder (automatisch)

wählbar: goodEncoder => goodTrains (halbautomatisch)

wählbar: goodEncoder, goodTrains => goodHypotheses (automatisch)

wählbar: goodEncoder => goodTests (halbautomatisch)

wählbar: goodTests goodEncoder, goodHypotheses => (automatisch)

kontinuierlich: => goodVerges (manuell)

kontinuierlich: goodVerges => goodTrains (manuell)

kontinuierlich: goodTrains => goodComplex (automatisch)

kontinuierlich: goodComplex, goodTrains => goodHypotheses (automatisch)

kontinuierlich: goodVerges => goodTests (manuell)

kontinuierlich: goodTests, goodComplex, goodHypotheses => (automatisch)



Die Bibliothek für maschinelles Lernen selbst heißt vkf.cpython -36m-x86_64-linux-gnu.so unter Linux oder vkf.cp36-win32.pyd unter Windows. (36 ist die Version von Python, für die diese Bibliothek erstellt wurde).



Der Begriff "automatisch" bedeutet die Arbeit dieser Bibliothek, "halbautomatisch" bedeutet die Arbeit der Hilfsbibliothek vkfencoder.cpython-36m-x86_64-linux-gnu.so. Schließlich ist der "manuelle" Modus ein Aufruf von Programmen, die speziell die Daten eines bestimmten Experiments verarbeiten und nun in die vkfencoder-Bibliothek übertragen werden.



3. Implementierungsdetails



Beim Erstellen eines Webservers verwenden wir den Ansatz "Ansicht / Modell / Steuerung"



. Python-Code befindet sich in 5 Dateien:



  1. app.py - Anwendungsstartdatei
  2. control.py - Datei mit Verfahren zum Arbeiten mit dem VKF-Solver
  3. models.py - Datei mit Klassen zum Verarbeiten von Daten und Arbeiten mit der Datenbank
  4. settings.py - Datei mit Anwendungseinstellungen
  5. views.py - Datei mit Visualisierung und Verarbeitung von Routen (Routen).


Die Datei app.py hat das Standardformular:



#! /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()


Ich denke nicht, dass hier etwas erklärt werden muss. Die nächste Datei in der Reihenfolge ihrer Aufnahme in das Projekt lautet 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()


Ich habe diese Datei für diesen Hinweis gekürzt und die Klassen, die die Dienstprogrammrouten bedienen, weggelassen:



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


Die verbleibenden Klassen behandeln die Routen, die für die maschinellen Lernschritte verantwortlich sind:



  1. Die Prepare-Klasse verarbeitet die Routen '/ experiment / {name}' und sammelt die Namen der Servicetabellen und numerischen Parameter, die zum Starten der Prozeduren der VKF-Methode erforderlich sind. Nach dem Speichern dieser Informationen in der Datenbank wird der Benutzer zur Route '/ vkf / {name}' umgeleitet.
  2. Die Generate-Klasse verarbeitet die Routen '/ vkf / {name}' und startet verschiedene Phasen des VKF-Methodeninduktionsverfahrens, abhängig von der Vorbereitung der Daten durch den Experten.
  3. Die Predict-Klasse verarbeitet die Routen '/ tests / {name}' und startet analog die Prozedur der VKF-Vorhersagemethode.


Um eine große Anzahl von Parametern an das Formular vkf.html zu übergeben, wird eine Konstruktion aus aiohttp_jinja2 verwendet



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




Beachten Sie auch die Verwendung des Spawn-Aufrufs aus dem Paket aiojobs.aiohttp:



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


Dies ist erforderlich, um Co-Prozeduren aus den in der Datei models.py definierten Klassen sicher aufzurufen, die Benutzerdaten und Experimente verarbeiten, die in der Datenbank unter der Kontrolle des MariaDB-DBMS gespeichert sind:



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)




Auch hier sind einige Hilfsklassen versteckt:



  1. Die Benutzerklasse entspricht dem Site-Besucher. Sie können sich als Experte registrieren und anmelden.
  2. In der Expertenklasse können Sie eines der Experimente auswählen.


Die übrigen Klassen entsprechen den Hauptverfahren:



  1. In der Experiment-Klasse können Sie die Namen der Schlüssel- und Hilfstabellen sowie die Parameter angeben, die für die Durchführung von ICF-Experimenten erforderlich sind.
  2. Die Solver-Klasse ist für die induktive Verallgemeinerung in der ICF-Methode verantwortlich.
  3. Die Predictor-Klasse ist für analoge Vorhersagen in der VKF-Methode verantwortlich.


Es ist wichtig, die Verwendung des Konstrukts create_pool () des Pakets aiomysql zu beachten. Sie können mit einer Datenbank in mehreren Verbindungen arbeiten. Die Routinen verify_future () und collect () aus dem Asyncio-Modul werden ebenfalls benötigt, um auf den Abschluss der Ausführung zu warten.



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()


Beim Lesen aus einer Tabelle gibt das Konstrukt row = cur.fetchone () die Zukunft zurück. Daher gibt row.result () einen Datenbankeintrag zurück, aus dem Feldwerte extrahiert werden können (z. B. ruft str (row.result () [2]) den Tabellennamen mit ab Codierung der Werte diskreter Merkmale).




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])


Wichtige Systemparameter werden aus der ENV-Datei oder (falls nicht vorhanden) aus der Datei settings.py importiert.



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)


Es ist wichtig zu beachten, dass localhost durch die IP-Adresse angegeben werden muss. Andernfalls versucht aiomysql, über einen Unix-Socket, der unter Windows möglicherweise nicht funktioniert, eine Verbindung zur Datenbank herzustellen. Spielen Sie abschließend die letzte Datei (control.py) ab:



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()


Ich habe diese Datei vollständig gespeichert, da hier die Namen, die Reihenfolge des Aufrufs und die Argumente der VKF-Methodenprozeduren aus der Bibliothek vkf.cpython-36m-x86_64-linux-gnu.so angezeigt werden. Alle Argumente nach dem Datenbanknamen können weggelassen werden, da die Standardeinstellungen in der CPython-Bibliothek mit Standardwerten festgelegt sind.



4. Kommentare



Angesichts der Frage professioneller Programmierer, warum die Logik der Steuerung des VKF-Experiments (durch zahlreiche Wenns) herausgearbeitet und nicht durch Polymorphismus in Typen verborgen wird, sollte die Antwort wie folgt lauten: Leider erlaubt die dynamische Typisierung der Python-Sprache keine Verschiebung der Entscheidung über den Typ des verwendeten Objekts auf das System Das heißt, diese Folge verschachtelter ifs wird sowieso auftreten. Daher bevorzugte der Autor die Verwendung einer expliziten (C-ähnlichen) Syntax, um die Logik so transparent (und effizient) wie möglich zu gestalten.



Lassen Sie mich die fehlenden Komponenten kommentieren:



  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.



Der Autor beschäftigt sich seit mehr als 30 Jahren mit Data Mining. Nach seinem Abschluss an der Fakultät für Mechanik und Mathematik der Moskauer Staatlichen Universität, benannt nach M.V. Lomonosov wurde er zu einer Gruppe von Forschern unter der Leitung von Doctor of Technical Sciences, Prof. Dr. VK. Finn (VINITI AN SSSR). Seit Anfang der 80er Jahre des letzten Jahrhunderts untersucht Viktor Konstantinovich plausibles Denken und dessen Formalisierung mittels mehrwertiger Logik.



Die von V.K. Finn, kann folgendes berücksichtigt werden:



  1. Verwenden einer binären Ähnlichkeitsoperation (anfänglich die Schnittoperation in der Booleschen Algebra);
  2. die Idee, die erzeugte Ähnlichkeit einer Gruppe von Trainingsbeispielen zu verwerfen, wenn sie in die Beschreibung eines Beispiels mit entgegengesetztem Vorzeichen eingebettet ist (Gegenbeispiel);
  3. die Idee, die untersuchte (Ziel-) Eigenschaft neuer Beispiele unter Berücksichtigung der Vor- und Nachteile vorherzusagen;
  4. die Idee, die Vollständigkeit einer Reihe von Hypothesen zu überprüfen, indem in Trainingsbeispielen Gründe (unter den generierten Ähnlichkeiten) für das Vorhandensein / Fehlen einer Zieleigenschaft gefunden werden.


Es sei darauf hingewiesen, dass V.K. Finn schreibt einige seiner Ideen ausländischen Autoren zu. Vielleicht wird nur die Logik der Argumentation von ihm zu Recht als unabhängig erfunden angesehen. Die Idee, Gegenbeispiele von V.K. Finn lieh ihm zufolge von K.R. Popper. Und die Ursprünge der Überprüfung der Vollständigkeit der induktiven Verallgemeinerung liegen bei ihm (meiner Meinung nach völlig undurchsichtig) den Werken des amerikanischen Mathematikers und Logikers C.S. Seebrücke. Er ist der Ansicht, dass die Generierung von Hypothesen über die Ursachen unter Verwendung der Ähnlichkeitsoperation aus den Ideen des britischen Ökonomen, Philosophen und Logikers D.S. Mühle. Daher schuf er eine Reihe von Ideen mit dem Titel "DSM-Methode" zu Ehren von D.S. Mühle.



Seltsam, tauchte aber in den späten 70er Jahren des 20. Jahrhunderts in den Werken von prof. Rudolf Wille (BRD), ein viel nützlicherer Abschnitt der algebraischen Gittertheorie "Analyse formaler Konzepte" (AFP), wird von V.K. Finns Grüße. Meiner Meinung nach ist der Grund dafür ein unglücklicher Name, der wie eine Person, die zuerst die Fakultät für Philosophie und dann den technischen Ablauf der Fakultät für Mechanik und Mathematik der Moskauer Staatlichen Universität abgeschlossen hat, Ablehnung hervorruft.



Als Nachfolger der Arbeit seines Lehrers nannte der Autor seinen Ansatz zu seinen Ehren "VKF-Methode". Es gibt jedoch eine andere Dekodierung - die probabilistisch-kombinatorische formale Methode des maschinellen Lernens, die auf der Theorie der Gitter basiert.



Jetzt V.K. Finna arbeitet im Ausstellungszentrum. A.A. Dorodnicyn RAS FRC IU RAS und am Institut für Intelligente Systeme der Russischen Staatlichen Universität für Geisteswissenschaften.



Weitere Informationen zur Mathematik des VKF-Lösers finden Sie in der Dissertation des Autors oder in seinen Videovorträgen an der Staatlichen Universität Uljanowsk (für die Organisation von Vorlesungen und die Bearbeitung ihrer Notizen ist der Autor A. B. Verevkin und N. G. Baranets dankbar).



Das vollständige Paket der Quelldateien wird auf Bitbucket gespeichert .



Die Quelldateien (in C ++) für die vkf-Bibliothek koordinieren derzeit ihre Platzierung auf savannah.nongnu.org. In diesem Fall wird hier ein Download-Link hinzugefügt.



Zum Schluss noch eine letzte Anmerkung: Ich habe am 6. April 2020 angefangen, Python zu lernen. Zuvor war die einzige Sprache, in der er programmierte, C ++. Dieser Umstand entbindet ihn jedoch nicht von den Anschuldigungen, dass der Code möglicherweise ungenau ist.



Der Autor bedankt sich bei Tatyana A. VolkovaRobofreakfür Unterstützung, konstruktive Vorschläge und Kritik, die es ermöglichten, die Präsentation signifikant zu verbessern (und sogar den Code signifikant zu verbessern). Die Verantwortung für die verbleibenden Fehler und getroffenen Entscheidungen (auch entgegen ihrem Rat) liegt jedoch allein beim Autor.



All Articles