Servidor web de aprendizaje automático "VKF-solver"

Ahora, a los ojos del público en general, el aprendizaje automático está fuertemente asociado con varias opciones para entrenar redes neuronales. Si inicialmente se trataba de redes totalmente conectadas, luego fueron reemplazadas por redes convolucionales y recurrentes, ahora se han convertido en opciones completamente exóticas como las redes GAN y LTSM. Además de los volúmenes crecientes de muestras requeridas para su capacitación, todavía sufren la imposibilidad de explicar por qué se tomó esta o aquella decisión. Pero también hay enfoques estructurales para el aprendizaje automático, cuya implementación de software se describe en este artículo.







Este es un enfoque doméstico para el aprendizaje automático, llamado método VKF de aprendizaje automático basado en la teoría de la red. La historia de origen y la elección del nombre se explican al final de este artículo.



1. Descripción del método.



Inicialmente, todo el sistema fue creado por el autor en C ++ como una aplicación de consola, luego se conectó a una base de datos bajo el control de MariaDB DBMS (usando la biblioteca mariadb ++), luego se convirtió en una biblioteca Python (usando el paquete pybind11).

Se seleccionaron varias matrices como datos de prueba para probar algoritmos de aprendizaje automático del repositorio de la Universidad de California en Irvine.



En el conjunto de hongos, que contiene descripciones de 8124 hongos norteamericanos, el sistema mostró resultados del 100%. Más precisamente, usando un generador de números aleatorios, los datos iniciales se dividieron en una muestra de entrenamiento (2088 comestibles y 1944 hongos venenosos) y una muestra de prueba (2120 comestibles y 1972 venenosos). Después de calcular alrededor de 100 hipótesis sobre las razones de la comestibilidad, todos los casos de prueba se predijeron correctamente. Dado que el algoritmo usa una cadena de Markov emparejada, puede variar un número suficiente de hipótesis. Muy a menudo resultó ser suficiente para generar 50 hipótesis aleatorias. Tenga en cuenta que al generar las causas de toxicidad, el número de hipótesis requeridas se agrupa alrededor de 120, sin embargo, todos los casos de prueba se predicen correctamente en este caso. Kaggle.com tiene una competencia de Clasificación de Hongosdonde bastantes autores han alcanzado el 100% de precisión. Pero la mayoría de las soluciones son redes neuronales. Nuestro enfoque permite que el recolector de hongos aprenda solo unas 50 reglas. Dado que la mayoría de las características son insignificantes, cada hipótesis será una conjunción de un pequeño número de valores de características esenciales, lo que las hace fáciles de recordar. Después de esto, el recolector de hongos puede ir a por hongos, sin tener miedo de tomar un hongo o saltear un hongo comestible.



Aquí hay un ejemplo de una de las hipótesis, sobre la base de la cual podemos suponer que el hongo es comestible:

[('gill_attachment', 'free'), ('gill_spacing', 'close'), ('gill_spacing', 'wide'), ('stalk_shape ',' ampliando '), (' stalk_surface_below_ring ',' scaly '), (' veil_type ',' partial '), (' veil_color ',' white '), ('ring_number ',' one '), (' ring_type ',' pendant ')]



Le llamo la atención sobre el hecho de que solo 9 de los 22 signos se muestran en la lista, ya que los 13 signos de similitud restantes no se observan en los hongos comestibles que dieron lugar a esta razón.



Otro conjunto fue SPECT Hearts. Allí, la precisión de la predicción de casos de prueba alcanzó el 86,1%, que resultó ser un poco más que los resultados (84%) del sistema de aprendizaje automático CLIP3, basado en aprender a cubrir ejemplos utilizando la programación entera utilizada por los autores de la matriz. Creo que debido a la estructura de la descripción de los tomogramas del corazón, que ya están precodificados allí con signos binarios, no es posible mejorar significativamente la calidad del pronóstico.



Recientemente, el autor ideó (e implementó en software) una extensión de su enfoque para procesar datos descritos por características continuas (numéricas). En algunos aspectos, su enfoque es similar al sistema C4.5 de árboles de decisión de capacitación. Esta variante se probó en la matriz de calidad del vino. Este conjunto describe la calidad de los vinos portugueses. Los resultados son alentadores: si tomamos vinos tintos de alta calidad, las hipótesis explican completamente sus altos puntajes.



2. Selección de plataforma



Actualmente, a través de los esfuerzos de los estudiantes del Departamento de Sistemas Inteligentes de la Universidad Estatal Humanitaria de Rusia, se está creando una serie de servidores web para varios tipos de tareas (utilizando el paquete Nginx + Gunicorn + Django).



Sin embargo, decidí describir mi versión personal aquí (usando los paquetes aiohttp, aiojobs y aiomysql). El módulo aiomcache no se usa debido a problemas de seguridad conocidos.



La opción propuesta tiene varias ventajas:



  1. es asíncrono debido al uso de aiohttp;
  2. permite la creación de plantillas Jinja2;
  3. funciona con un grupo de conexiones a la base de datos a través de aiomysql;
  4. permite que se inicien procesos computacionales independientes a través de aiojobs.aiohttp.spawn.


Señalemos también las desventajas obvias (en comparación con Django):



  1. sin mapeo relacional de objetos (ORM);
  2. es más difícil organizar el uso del servidor proxy Nginx;
  3. sin lenguaje de plantilla Django (DTL).


Cada una de las dos opciones está dirigida a diferentes estrategias para trabajar con un servidor web. La estrategia sincrónica (en Django) está dirigida a un modo de usuario único, en el que un experto trabaja con una sola base de datos a la vez. Aunque los procedimientos probabilísticos del método VKF son notablemente paralelos, sin embargo, existe la posibilidad teórica de que los procedimientos de aprendizaje automático tomen un tiempo considerable. Por lo tanto, la opción discutida en esta nota está dirigida a varios expertos, cada uno de los cuales puede trabajar simultáneamente (en diferentes pestañas del navegador) con diferentes bases de datos que difieren no solo en los datos, sino también en la forma en que se presentan (diferentes redes en los valores de características discretas, diferentes regresiones significativas y el número umbrales para continuo). Luego, al comenzar el experimento VKF en una pestaña, el experto puede cambiar a otra,donde preparará o analizará un experimento con diferentes datos y / o parámetros.



Para tener en cuenta varios usuarios, experimentos y diferentes etapas en las que se encuentran, hay una base de datos de servicio (vkf) con dos tablas (usuarios, experimentos). Si la tabla de usuario almacena el inicio de sesión y la contraseña de todos los usuarios registrados, los experimentos, además de los nombres de las tablas auxiliares y principales de cada experimento, mantienen el estado de finalización de estas tablas. Abandonamos aiohttp_session ya que todavía necesitamos usar un proxy Nginx para proteger datos críticos.



Aquí está la estructura de la tabla de experimentos:



  • id int (11) NOT NULL PRIMARY KEY
  • expName varchar (255) NO NULL
  • codificador varchar (255)
  • goodEncoder tinyint (1)
  • celosías varchar (255)
  • goodLattices tinyint (1)
  • varchar complejo (255)
  • goodComplex tinyint (1)
  • verges varchar (255)
  • goodVerges tinyint (1)
  • vergesTotal int (11)
  • trenes varchar (255) NO NULL
  • goodTrains tinyint (1)
  • tests varchar(255)
  • goodTests tinyint(1)
  • hypotheses varchar(255) NOT NULL
  • goodHypotheses tinyint(1)
  • type varchar(255) NOT NULL


Cabe señalar que hay algunas secuencias de preparación de datos para los experimentos de VKF, que, lamentablemente, son radicalmente diferentes para casos discretos y continuos. El caso de caso mixto combina los requisitos de ambos tipos.



discreto: => goodLattices (semiautomático)

discreto: goodLattices => goodEncoder (automático)

discreto: goodEncoder => goodTrains (semiautomático)

discreto: goodEncoder, goodTrains => goodHypotheses (automático)

discreto: goodEncoder => goodTests (semiautomático),

discreto goodEncoder, goodHypotheses => (automático)

continuo: => goodVerges (manual)

continuo: goodVerges => goodTrains (manual)

continuo: goodTrains => goodComplex (automático)

continuo: goodComplex, goodTrains => goodHypotheses (automático)

continuo: goodVerges => goodTests (manual)

continuo: goodTests, goodComplex, goodHypotheses => (automático)



La biblioteca de aprendizaje automático se llama vkf.cpython -36m-x86_64-linux-gnu.so en Linux o vkf.cp36-win32.pyd en Windows. (36 es la versión de Python para la que se creó esta biblioteca).



El término "automático" significa el funcionamiento de esta biblioteca, "semiautomático" significa el funcionamiento de la biblioteca auxiliar vkfencoder.cpython-36m-x86_64-linux-gnu.so. Finalmente, el modo "manual" es una llamada de programas que procesan especialmente los datos de un experimento en particular y ahora se transfieren a la biblioteca vkfencoder.



3. Detalles de implementación



Al crear un servidor web, utilizamos el enfoque "Ver / Modelo / Control"



. El código de Python se encuentra en 5 archivos:



  1. app.py: archivo de inicio de la aplicación
  2. control.py: archivo con procedimientos para trabajar con el solucionador VKF
  3. models.py: archivo con clases para procesar datos y trabajar con la base de datos
  4. settings.py - archivo de configuración de la aplicación
  5. views.py - archivo con visualización y manejo de rutas (rutas).


El archivo app.py tiene la forma estándar:



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


No creo que haya nada que explicar aquí. El siguiente archivo en el orden de inclusión en el proyecto es 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()


He acortado este archivo por el bien de esta nota, dejando de lado las clases que sirven las rutas de utilidad:



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


Las clases restantes procesan las rutas responsables de las etapas del aprendizaje automático:



  1. la clase Preparar procesa las rutas '/ experiment / {name}' y recopila los nombres de las tablas de servicio y los parámetros numéricos necesarios para iniciar los procedimientos del método VKF. Después de guardar esta información en la base de datos, el usuario es redirigido a la ruta '/ vkf / {name}'.
  2. la clase Generate procesa las rutas '/ vkf / {name}' e inicia las diversas etapas del procedimiento de inducción del método VKF, dependiendo de la preparación de los datos por parte del experto.
  3. la clase Predict procesa las rutas '/ tests / {name}' e inicia el procedimiento del método de predicción VKF por analogía.


Para pasar una gran cantidad de parámetros al formulario vkf.html, se utiliza una construcción de aiohttp_jinja2



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




También tenga en cuenta el uso de la llamada de generación del paquete aiojobs.aiohttp:



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


Esto es necesario para llamar de forma segura a las corutinas de las clases definidas en el archivo models.py que procesan los datos de usuarios y experimentos almacenados en un DB administrado por MariaDB:



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)




Nuevamente, algunas clases auxiliares están ocultas:



  1. La clase de usuario corresponde al visitante del sitio. Le permite registrarse e iniciar sesión como experto.
  2. La clase Expert le permite elegir uno de los experimentos.


Las clases restantes corresponden a los procedimientos principales:



  1. La clase Experimento le permite especificar los nombres de las tablas clave y auxiliares y los parámetros necesarios para realizar experimentos ICF.
  2. La clase Solver es responsable de la generalización inductiva en el método ICF.
  3. La clase Predictor es responsable de las predicciones por analogía en el método CCF.


Es importante tener en cuenta el uso de la construcción create_pool () del paquete aiomysql. Le permite trabajar con una base de datos en varias conexiones. Las rutinas sure_future () y collect () del módulo asyncio también son necesarias para esperar a que se complete la ejecución.



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


Al leer desde una tabla, row = cur.fetchone () devuelve un futuro, por lo que row.result () devuelve un registro de base de datos desde el que se pueden recuperar los valores de campo (por ejemplo, str (row.result () [2]) recupera el nombre de la tabla con codificación de los valores de características discretas).




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


Los parámetros clave del sistema se importan del archivo .env o (si falta) del archivo 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)


Es importante tener en cuenta que localhost debe especificarse mediante la dirección IP; de lo contrario, aiomysql intentará conectarse a la base de datos a través de un socket Unix, que puede no funcionar en Windows. Finalmente, reproduzcamos el último archivo (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()


Guardé este archivo en su totalidad, ya que aquí puede ver los nombres, el orden de las llamadas y los argumentos de los procedimientos del método VKF desde la biblioteca vkf.cpython-36m-x86_64-linux-gnu.so. Todos los argumentos después de dbname se pueden omitir, ya que los valores predeterminados en la biblioteca CPython se establecen con valores estándar.



4. Comentarios



Anticipando la pregunta de los programadores profesionales acerca de por qué la lógica de controlar el experimento VKF se presenta (a través de numerosos ifs), y no se oculta a través del polimorfismo en tipos, la respuesta debería ser la siguiente: desafortunadamente, la tipificación dinámica del lenguaje Python no permite cambiar la decisión sobre el tipo de objeto utilizado para el sistema , es decir, esta secuencia de ifs anidados ocurrirá de todos modos. Por lo tanto, el autor prefirió usar una sintaxis explícita (similar a C) para hacer que la lógica sea lo más transparente (y eficiente) posible.



Déjame comentar sobre los componentes que faltan:



  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.



El autor ha estado involucrado en tareas de minería de datos durante más de 30 años. Después de graduarse de la Facultad de Mecánica y Matemáticas de la Universidad Estatal de Moscú Lomonosov, fue invitado a un grupo de investigadores bajo el liderazgo del Doctor en Ciencias Técnicas, prof. VK. Finn (VINITI AN SSSR). Desde principios de los años 80 del siglo pasado, Viktor Konstantinovich ha estado explorando razonamientos plausibles y su formalización mediante lógicas de valores múltiples.



Las ideas clave propuestas por V.K. Finn, se puede considerar lo siguiente:



  1. utilizando una operación de similitud binaria (inicialmente, la operación de intersección en álgebra booleana);
  2. la idea de descartar la similitud generada de un grupo de ejemplos de entrenamiento si está incrustado en la descripción de un ejemplo del signo opuesto (contraejemplo);
  3. la idea de predecir las propiedades investigadas (objetivo) de nuevos ejemplos teniendo en cuenta los pros y los contras;
  4. La idea de verificar la integridad de muchas hipótesis mediante la búsqueda de las causas (entre las similitudes generadas) para la presencia / ausencia de la propiedad objetivo en ejemplos educativos.


Cabe señalar que V.K. Finn atribuye algunas de sus ideas a autores extranjeros. Quizás, solo la lógica de la argumentación con pleno derecho se considera inventada por él de manera independiente. La idea de contabilizar los contraejemplos de V.K. Finn tomó prestado, dijo, de K.R. Corchete. Y las fuentes para verificar la integridad de la generalización inductiva se relacionan con ellos (completamente vagos, en mi opinión) los trabajos del matemático y lógico estadounidense Ch.S. Atravesar. Considera que la generación de hipótesis sobre las causas que utilizan la operación de similitud se debe tomar de las ideas del economista, filósofo y lógico británico D.S. Molino. Por lo tanto, creó un conjunto de ideas que tituló "Método DSM" en honor a D.S. Molino.



Extraño, pero surgió a finales de los años 70 del siglo XX en las obras del prof. Rudolf Wille (FRG), una sección mucho más útil de la teoría algebraica de las redes "Análisis de conceptos formales" (AFP) no es utilizada por V.K. Saludos de Finn. En mi opinión, la razón de esto es el desafortunado nombre que, como persona que primero se graduó de la Facultad de Filosofía, y luego la corriente de ingeniería de la Facultad de Mecánica y Matemáticas de la Universidad Estatal de Moscú, causa rechazo.



Como seguidor del trabajo de su maestro, el autor llamó a su enfoque "método VKF" en su honor. Sin embargo, hay otra decodificación: el método formal probabilístico-combinatorio de aprendizaje automático basado en la teoría de las redes.



Ahora V.K. Finn trabaja en el CC ellos. AUTOMÓVIL CLUB BRITÁNICO. Dorodnicyn RAS FRC IU RAS y en el Departamento de Sistemas Inteligentes de la Universidad Estatal de Rusia para las Humanidades.



Se puede encontrar más información sobre las matemáticas del solucionador de VKF en la disertación del autor o en sus video conferencias en la Universidad Estatal de Ulyanovsk (por organizar conferencias y procesar sus notas, el autor agradece a A.B. Verevkin y N.G. Baranets).



El paquete completo de archivos fuente se almacena en Bitbucket .



Los archivos fuente (en C ++) para la biblioteca vkf están en proceso de acordar su ubicación en savannah.nongnu.org. En caso afirmativo, se agregará un enlace de descarga aquí.



Finalmente, una nota final: comencé a aprender Python el 6 de abril de 2020. Antes de esto, el único lenguaje en el que programaba era C ++. Pero esta circunstancia no lo libera de las acusaciones de que el código puede ser inexacto.



El autor agradece a Tatyana A. Volkova.robofreakpara apoyo, sugerencias constructivas y críticas, que hicieron posible mejorar significativamente la presentación (e incluso mejorar significativamente el código). Sin embargo, la responsabilidad de los errores restantes y las decisiones tomadas (incluso en contra de su consejo) es únicamente del autor.



All Articles