Восстановление логической функции

в 10:51, , рубрики: python, Алгоритмы, индуктивная логика, искусственный интеллект, машинное обучение, нейрон, теорема Байеса, метки: , , ,
image

В данной статье Вы сможете найти готовую реализацию и описание алгоритма предназначенного для реконструкции логических функций методом чёрного ящика. Под логической функцией я подразумеваю такую функцию, которая принимает в качестве аргументов множество булевых значений и соответственно возвращает одно. Пример:

def customlogic(params):
    return params[0] and params[1] and not params[5] and params[11] or params[2] and not params[3] or params[0] and params[5] and not params[6] or params[7] and not params[8]

В конце статьи алгоритм проверяется на данных полученных из реального мира.

По моему мнению, данная задача тесно связана с нейрофизиологией и как Вы могли уже догадаться я вдохновлялся работой нейронов, поэтому будут использованы аналогии связанные с работой и строением данного типа клеток. В частности, желание что-нибудь накодить пришло после прочтения статьи об устройстве и функционировании дендритов. Хоть бОльшую часть материала я не усвоил, но провёл некоторые аналогии.

  • Дендритные деревья выполняют логическую операцию AND, так как потенциалы приходящие от ветвей суммируются, а если нет сигнала от одной или нескольких ветвей, то скорее всего потенциал не дойдёт до сомы.
  • Cома выполняет OR, генерируя потенциал действия если хотя бы одно дерево доставило сигнал.
  • Ингибиторные(тормозящие) связи являются аналогом NOT. Правда есть баг, с точки зрения логики: NOT False = False, так как отсутствие сигнала от тормозящей связи само по себе не способно вызывать потенциал действия. Что сужает круг возможных функций, до тех которые при всех неактивных параметрах(False) не генерируют True. Следовательно единичный нейрон не может симулировать логическое отрицание, но вполне способен сделать XOR. Такое поведение хоть и является «странным», но несёт некоторые выгоды. Данный подход является довольно энергоэффективным, ведь если держать нейрон включённым когда все дендриты не активны — значить тратить энергию впустую. Конечно есть исключения, нейроны которые таки периодически включаются без стимуляции дендритов. Грубо говоря, можно симулировать AND NOT, но OR NOT нельзя

Думаю достаточно лирики, перейдём к делу.

Исходный код класса и пример

import random
class LogicReconstructer:
    groups=[]
    threshold = 0.99
    maxmem = 10
    numparams = 0

    def __init__(self,numparams,threshold = 0.99, totalmem = 10):
        self.numparams=numparams
        self.threshold=threshold
        self.maxmem=totalmem

    def getactive(self,params):
        if len(params)!=self.numparams:
            raise Exception("LogicReconstructer: numparams mismatch")

        active=[]
        for x in range(self.numparams):
            if params[x]:
                active.append(x)
        return  active

    def extractgroups(self,params,result):
        active=self.getactive(params)
        exist = False
        ignore = False

        if result and active:
            ind=0
            while ind<len(self.groups):

                if len(active)>len(self.groups[ind][0]) and self.issublist(self.groups[ind][0],active):
                    ignore=True
                    break

                elif len(active)==len(self.groups[ind][0]) and self.issublist(active,self.groups[ind][0]):
                    exist=True
                    break

                elif len(active)<len(self.groups[ind][0]) and self.issublist(active,self.groups[ind][0]):
                    del self.groups[ind]
                    ind-=1

                ind+=1

            if not exist and not ignore:
                self.groups.append([active,[0]*self.numparams,False])

    def extractinhibitors(self,params,result):
        active=self.getactive(params)

        if result:
            count=0
            for _,grp in enumerate(self.groups):
                if self.issublist(grp[0],active):
                    count+=1
                    if count>1:
                        return

        for _,grp in enumerate(self.groups):
            if not grp[2] and self.issublist(grp[0],active):

                neg=[]
                negvalue=False
                for y in range(self.numparams):
                    if grp[1][y]<=-self.threshold:
                        neg.append(y)
                        negvalue|=params[y]
                    elif grp[1][y]>=self.threshold:
                        grp[2]=True

                for y in range(self.numparams):
                    if params[y]:
                        if y in neg or not negvalue:
                            grp[1][y] = self.counting(grp[1][y],self.maxmem,result)


    def counting(self,prc,total,item):
        result=prc-prc/total
        if not item:
            result-=1/total
        else:
            result+=1/total
        return result

    def issublist(self,a,b):
        for ind,item in enumerate(a):
            if item not in b:
                return False
        return True

    def getsublist(self,a,b):
        result=[]
        for ind,item in enumerate(a):
            if item in b:
                result.append(item)
        return result

    def simulate(self,params):
        result=False
        for ind,item in enumerate(self.groups):
            if item[2]:
                locres=True
                for x in range(len(item[0])):
                    locres&=params[item[0][x]]
                for x in range(len(item[1])):
                    if item[1][x]<=-self.threshold:
                        locres=locres&~params[x]
                result|=locres
        return result

    def getlogicfunc(self,guess=False):
        result=""
        for ind,item in enumerate(self.groups):
            if item[2] or guess:
                locres=""
                for x in range(len(item[0])):
                    if x!=0:
                        locres+=" and "
                    locres+=str(item[0][x])
                for x in range(len(item[1])):
                    if item[1][x]<=-self.threshold:
                        locres+=" and not "+str(x)
                if ind!=0:
                    result+=" or "
                result+=locres
        return result

    def randparams(self):
        result = []
        for x in range(self.numparams):
            result.append(random.choice([True, False]))
        return result

    def isready(self):
        result=bool(self.groups)
        for ind,item in enumerate(self.groups):
            result&=item[2]
        return result

    def getlogicstuct(self):
        result = []
        for _,item in enumerate(self.groups):
            grp=[]
            if item[2]:
                for x in range(len(item[0])):
                    grp.append([item[0][x],True])
                for x in range(len(item[1])):
                    if item[1][x]<=-self.threshold:
                        grp.append([x,False])
            if grp:
                result.append(grp)
        return result

    def simulatebystruct(self,params,grps):
        for _,item in enumerate(grps):
            locres=True
            for _,param in enumerate(item):
                if param[1]:
                    locres&=params[param[0]]
                else:
                    locres&=~params[param[0]]
                if not locres:
                    break
            if locres:
                return True

        return  False

def customlogic(params):
    return params[0] and  params[1] and not params[5]and  params[11] or params[2] and not params[3]  
               or params[0] and  params[5] and not params[6] or  params[7] and not params[8]

def newme():
    numparams = 12
    neuron=LogicReconstructer(numparams)
    while not neuron.isready():
        params=neuron.randparams()
        funcresult=customlogic(params)
        neuron.extractgroups(params,funcresult)
        neuron.extractinhibitors(params,funcresult)

    for x in range(1000):
        params=neuron.randparams()
        if neuron.simulate(params)!=customlogic(params):
            print("Simulate is wrong.")
            return

    print(neuron.getlogicfunc())

if __name__ == "__main__":
    newme()

Итак, мы имеем набор входных параметров, причём некоторые из них могут никак не влиять на результат выполнения, поэтому первым делом нужно понять какие параметры важны. Лучше начать со сбора независимых активационных групп, другими словами если результат работы функции — True, то записываем номера активных параметров в базу, с некоторыми оговорками, которые Вы можете посмотреть в методе extractgroups. Таким образом мы найдем параметры связанные логическим оператором AND. Этот этап можно сравнить с отращиванием дендритов.

Теперь же перейдем к настройке синапсов и прежде всего нужно выделить тормозящие связи. Так как группы являются условно независимыми от тормозящих связей, то значит можно воспользоваться теоремой Байеса. Иными словами если активационная группа сработала, то подсчитываем влияние остальных параметров на результат. Если параметр негативно влияет на результат, т.е. при его активации результат в большинстве случаев был False, то он и является тормозящим(AND NOT). Подробности смотрим в методе extractinhibitors.

Работа алгоритма считается завершённой когда, все группы получили подтверждение того, что все тормозящие связи были найдены. Подтверждение заключается в том, что Байесова вероятность одного из параметров перешагнула через threshold.

Но допустим, что у нас не хватает каких-то важных параметров, с которыми работает функция, то как это определить? Например:

params[0] and params[1] and not params[2] or params[3] and random.choice([True, False])

Мы знаем про 4 параметра, но не учитываем пятый. Конкретно в этом случаи активационная группа, которая состоит лишь из параметра 3, не переступит порог срабатывания(threshold). Но если бы мы всё-таки его учли, то он находился бы в одной группе с 3-им. Как Вы поняли группы разделяются логическим оператором OR.

А вот если не хватает целой группы:

params[0] and params[1] and not params[2] or random.choice([True, False]) and random.choice([True, False])

то группы(простите за тавтологию) при анализе разделяются на единичные параметры и каждый параметр станет активационным, что и является признаком.

Попробуйте поиграться с customlogic, дабы убедиться, что алгоритм работает. Я например заметил, что при большом количестве тормозящих связей в группе, она может не определиться.

Бонусная C++ простыня

// neuro.cpp: определяет точку входа для консольного приложения.
//

#include "stdafx.h"
#include <vector>
#include <map>
#include <stdlib.h>
#include <time.h>
#include <iostream>

using namespace std;

class Bool
{
public:

	Bool(): m_value(){}
	Bool( bool value ) : m_value(value){}

	operator bool() const { return m_value;}

	bool* operator& () { return &m_value; }
	const bool * const operator& () const { return &m_value; }

private:

	bool m_value;

};

class neuron
{
public:
	double threshold;
	int synapticmem;
	int numparams;
	map<int,vector<char>> groups;
	map<int,vector<double>> bayesian;
	map<int,map<char,bool>> grouplogic;
	int maxid;

	neuron(int numparams, double threshold = 0.99, int synapticmem = 10)
	{
		this->numparams=numparams;
		this->threshold=threshold;
		this->synapticmem=synapticmem;
		maxid=0;
		srand ( time(NULL) );
	}
	~neuron()
	{

	}
	
	vector<char> getactive(vector<Bool>& params)
	{
		if (params.size()!=numparams)
			throw 1;// numparams mismatch
		
		vector<char> active;
		for (int i=0;i<this->numparams;i++)
			if (params[i])
				active.push_back((char)i);

		return active;
	}

	void extractgroups(vector<Bool>& params, bool result)
	{
		vector<char> active= this->getactive(params);
		bool ignore = false;
		bool exist = false;
		
		if (result && active.size()>0)
		{
			map<int,vector<char>>::iterator i=this->groups.begin();
			while (i!=this->groups.end())
			{
				if (active.size()>i->second.size()&&this->issublist(i->second,active))
					{
						ignore=true;
						break;
					}
				else if (active.size()==i->second.size()&&this->issublist(active,i->second))
					{
						exist=true;
						break;
					}
				else if (active.size()<i->second.size()&&this->issublist(active,i->second))
					{
						this->bayesian.erase(i->first);
						this->grouplogic.erase(i->first);
						i=this->groups.erase(i);
					}
					else
						i++;
			}
			if (!exist && !ignore)
			{
				this->groups[this->maxid]=active;
				this->bayesian[this->maxid]=*new vector<double>(numparams,0);
				this->maxid++;
			}
		}
	}

	template< class T >
	bool issublist(vector<T>& sublist, vector<T>& fulllist)
	{
		for (int i=0;i<sublist.size();i++)
		{
			bool match = false;
			for (int n=0;n<fulllist.size();n++)
				if (sublist[i]==fulllist[n])
				{
					match=true;
					break;
				}
			if (!match)
				return false;	
		}
		return true;
	}

	template<class InputIterator, class T>
	InputIterator find (InputIterator first, InputIterator last, const T& val)
	{
		while (first!=last) {
			if (*first==val) return first;
			++first;
		}
		return last;
	}

	void extractinhibitors(vector<Bool>& params, bool result)
	{
		vector<char> active= this->getactive(params);

		if (result)
		{
			bool count = false;
			for (map<int,vector<char>>::iterator i=this->groups.begin();i!=this->groups.end();++i)
				if(this->issublist(i->second,active))
					if (!count)
						count=true;
					else
						return;
		}

		for (map<int,vector<char>>::iterator i=this->groups.begin();i!=this->groups.end();++i)
			if (grouplogic.count(i->first)==0)  
				if (this->issublist(i->second,active))
				{
					vector<char> neg;
					bool negvalue = false;
					for (int n=0;n<this->numparams;n++)
						if (this->bayesian[i->first][n]<=-1*this->threshold)
						{
							neg.push_back((char)n);
							negvalue|=params[n];
						}
						else if (this->bayesian[i->first][n]>=this->threshold)
							if (grouplogic.count(i->first)==0)
								this->build_grouplogic(i->first);
					
					for (int n=0;n<this->numparams;n++)
						if (params[n])
							if (!negvalue || this->find(neg.begin(), neg.end(), n) != neg.end())
 									this->bayesian[i->first][n]=counting(this->bayesian[i->first][n],result);
				}
	}

	 double counting(double prc, bool result)
	 {
		 double oneless = prc - prc/this->synapticmem;
		 if (result)
			 oneless+=1.0/this->synapticmem;
		 else
			 oneless-=1.0/this->synapticmem;
		 return oneless;
	 }
	void build_grouplogic(int indgroup)
	{
		for (int i=0;i<this->groups[indgroup].size();i++)
			 this->grouplogic[indgroup][(char)groups[indgroup][i]]=true;
		
		for (int i=0;i<numparams;i++)
			if (this->bayesian[indgroup][i]<=-1*threshold)
				this->grouplogic[indgroup][(char)i]=false;
	}
	bool isready()
	{
		int count = 0;
		for (map<int,vector<char>>::iterator i=this->groups.begin();i!=this->groups.end();++i) 
			if (grouplogic.count(i->first)!=0)
				count++;
		if (count==groups.size()&&groups.size()>0)
			return true;
		return false;
	}
	bool randomBool() {
		return rand() % 2 == 1;
	}
	void randparams(vector<Bool>& params)
	{
		for (int i=0;i<this->numparams;i++)
			params[i]=this->randomBool();
	}
	string getfunction()
	{
		string result="";
		bool maincount=false;
		for (map<int,map<char,bool>>::iterator i=this->grouplogic.begin();i!=this->grouplogic.end();++i)
		{	
			if (maincount)
				result+=" or ";

			string locres="";
			bool count = false;
			for (map<char,bool>::reverse_iterator n=i->second.rbegin();n!=i->second.rend();++n)
			{
				if (count)
					locres+=" and ";
				if (!n->second)
					locres+="not ";
				locres+=(char)(((int)'0')+(int)n->first);
				count=true;
			}
			result+=locres;
			maincount=true;
		}
		return result;
	}

	bool simulate(vector<Bool>& params)
	{
		bool result=false;
		for (map<int,map<char,bool>>::iterator i=this->grouplogic.begin();i!=this->grouplogic.end();++i)
		{	
			bool locres=true;
			bool count = false;
			for (map<char,bool>::iterator n=i->second.begin();n!=i->second.end();++n)
			{
				if (n->second)
					locres&=params[(int)n->first];
				else
					locres&=!params[(int)n->first];
			}
			result|=locres;
		}
		return result;
	}

};

bool customlogic(vector<Bool>& params)
{
	return params[0] && params[1] && !params[2] || params[3] && !params[1] || params[5] && !params[0] && params[1];
}

int _tmain(int argc, _TCHAR* argv[])
{
	int numparams = 6;
	
	clock_t start = clock() ;
	
	neuron* nine=new neuron(numparams);
	while (!nine->isready())
	{
		vector<Bool> params(numparams, false);
		nine->randparams(params);
		bool result = customlogic(params);
		nine->extractgroups(params,result);
		nine->extractinhibitors(params,result);
	}
	clock_t end = clock();
	double elapsed_time = (end-start)/(double)CLOCKS_PER_SEC;
	
	for (int i=0;i<1000;i++)
	{	
		vector<Bool> params(numparams, false);
		nine->randparams(params);
		if (nine->simulate(params)!=customlogic(params))
		{
			printf("Simulate is wrong");
			return 0;
		}
	}

	printf("%sn%.3f secn",nine->getfunction().c_str(),elapsed_time);

	getchar();
	return 0;
}

Эксперимент

Конечно хорошо, что алгоритм работает, но справиться ли он с данными из реального мира? Я решил это проверить на одном наборе данных взятом отсюда. Задача заключается в том, чтобы классифицировать фальшивые банкноты от настоящих по четырём параметрам. Надо сказать я совсем не понял что, они означают. Впрочем это только на руку чистоте эксперимента, ведь обучаться должна машина, а не я.

Вот что получилось

def getbinary(num,max,min =0 ):
    result=[]
    binnums=int(math.ceil(math.log(abs(min-max),2)))
    binstr=('{0:0'+str(binnums)+'b}').format(-min+num)
    for _,item in enumerate(binstr):
        if item=='1':
            result.append(True)
        else:
            result.append(False)
    return result

def banknotes():
    file = open('C:/bankdata.txt', 'r')# http://archive.ics.uci.edu/ml/datasets/banknote+authentication
    lines = file.readlines()
    file.close()

    data = []

    for i in range(len(lines)):
        data.append(lines[i].strip().split(","))
    lines.clear()

    numdata = []
    paramsmax=[0,0,0,0]
    paramsmin=[0,0,0,0]
    for i in range(len(data)):
        tmp=[]
        for x in range(len(data[i])):
            if x!=4:
                tmp.append(int(round(float(data[i][x]))))
                paramsmax[x]=max(paramsmax[x],int(round(float(data[i][x]))))
                paramsmin[x]=min(paramsmin[x],int(round(float(data[i][x]))))
            else:
                if data[i][x]=='0':
                    tmp.append(False)
                else:
                    tmp.append(True)
        numdata.append(tmp)
    data.clear()


    bindata=[]
    for i in range(len(numdata)):
        tmp=[]
        for x in range(len(numdata[i])):
            if x!=4:
                tmp.extend(getbinary(numdata[i][x],paramsmax[x],paramsmin[x]))
            else:
                tmp.extend([numdata[i][x]])
        bindata.append(tmp)
    numdata.clear()

    neuron = LogicReconstructer(len(bindata[0])-1,totalmem=7, threshold=0.98)

    for _,item in enumerate(bindata):
        neuron.extractgroups(item[:-1],item[-1:][0])

    ready=False
    while not neuron.isready():
        rnd=random.randint(0,len(bindata)-1)
        neuron.extractinhibitors(bindata[rnd][:-1],bindata[rnd][-1:][0])

    logicstruct=neuron.getlogicstuct()
    print(logicstruct)

    falsepositive = 0
    falsenegative = 0
    for _,item in enumerate(bindata):
       res = neuron.simulatebystruct(item[:-1],logicstruct)
       if res!=item[-1:][0]:
           if res:
               falsepositive+=1
           else:
               falsenegative+=1
    print(falsenegative/len(bindata),falsepositive/len(bindata))

Так как алгоритм принимает только бинарные данные мне пришлось округлить числа до целых и перевести в бинарную форму функцией getbinary. После обучения у меня вышла вот такая структура состоящая из 134-ёх логических групп:

logicstruct

    logicstr="[[[2, True], [3, True], [6, True], [7, True], [10, True], [11, True], [12, True], [13, True], [14, True], [16, True]], [[1, True], [3, True], [4, True], [8, True], [12, True], [15, True], [16, True], [17, True]], [[1, True], [3, True], [5, True], [10, True], [12, True], [14, True], [16, True], [6, False], [7, False], [8, False], [11, False]], [[1, True], [2, True], [5, True], [6, True], [7, True], [11, True], [13, True], [14, True], [16, True], [10, False]], [[1, True], [2, True], [5, True], [6, True], [11, True], [12, True], [13, True], [14, True], [16, True]], [[2, True], [3, True], [8, True], [9, True], [12, True], [15, True], [16, True]], [[1, True], [2, True], [3, True], [4, True], [8, True], [15, True], [17, True], [6, False], [7, False], [11, False]], [[1, True], [4, True], [7, True], [8, True], [11, True], [15, True], [2, False], [6, False]], [[1, True], [6, True], [10, True], [11, True], [12, True], [15, True], [16, True], [17, True]], [[0, True], [5, True], [6, True], [7, True], [8, True], [12, True], [14, True], [17, True], [10, False], [11, False], [13, False]], [[0, True], [4, True], [7, True], [15, True], [17, True], [6, False], [16, False]], [[1, True], [3, True], [5, True], [10, True], [12, True], [14, True], [17, True], [2, False], [6, False], [7, False]], [[0, True], [5, True], [6, True], [7, True], [12, True], [13, True], [14, True], [16, True], [2, False], [11, False]], [[0, True], [4, True], [7, True], [8, True], [16, True], [17, True], [6, False], [15, False]], [[1, True], [4, True], [8, True], [11, True], [13, True], [14, True], [2, False]], [[0, True], [4, True], [12, True], [14, True], [17, True], [6, False], [11, False], [13, False]], [[2, True], [3, True], [4, True], [8, True], [11, True], [15, True], [17, True], [1, False], [6, False], [7, False]], [[1, True], [3, True], [5, True], [6, True], [7, True], [11, True], [14, True], [2, False], [10, False], [12, False]], [[1, True], [2, True], [4, True], [12, True], [14, True], [11, False]], [[1, True], [6, True], [7, True], [8, True], [9, True], [14, True]], [[2, True], [5, True], [9, True], [14, True]], [[1, True], [4, True], [7, True], [11, True], [13, True], [14, True], [2, False], [3, False], [8, False], [12, False], [17, False]], [[1, True], [3, True], [5, True], [8, True], [10, True], [11, True], [14, True], [17, True], [6, False]], [[2, True], [3, True], [6, True], [7, True], [9, True], [13, True], [14, True]], [[1, True], [5, True], [6, True], [8, True], [11, True], [12, True], [13, True], [14, True], [16, True], [0, False], [7, False]], [[2, True], [3, True], [7, True], [9, True], [11, True], [14, True]], [[1, True], [5, True], [6, True], [7, True], [11, True], [12, True], [14, True], [16, True], [0, False], [8, False]], [[1, True], [3, True], [4, True], [8, True], [12, True], [13, True], [15, True], [16, True], [2, False], [5, False], [6, False], [11, False]], [[1, True], [6, True], [8, True], [10, True], [11, True], [12, True], [14, True]], [[1, True], [2, True], [4, True], [8, True], [13, True], [15, True], [16, True], [6, False]], [[1, True], [2, True], [5, True], [6, True], [10, True], [14, True], [16, True], [11, False], [13, False]], [[2, True], [3, True], [4, True], [7, True], [8, True], [11, True], [15, True], [1, False], [6, False], [17, False]], [[1, True], [6, True], [10, True], [11, True], [13, True], [15, True], [16, True], [17, True]], [[1, True], [2, True], [5, True], [7, True], [11, True], [12, True], [13, True], [14, True], [16, True]], [[2, True], [4, True], [6, True], [8, True], [11, True], [13, True], [16, True], [0, False], [15, False]], [[1, True], [5, True], [6, True], [7, True], [10, True], [14, True], [17, True], [2, False], [8, False], [11, False], [12, False]], [[1, True], [5, True], [10, True], [12, True], [13, True], [14, True], [16, True]], [[0, True], [4, True], [7, True], [15, True], [16, True], [6, False], [17, False]], [[1, True], [2, True], [3, True], [4, True], [7, True], [8, True], [16, True], [17, True], [6, False]], [[1, True], [2, True], [5, True], [6, True], [11, True], [14, True], [17, True], [10, False], [12, False]], [[1, True], [2, True], [4, True], [8, True], [12, True], [15, True], [16, True]], [[1, True], [4, True], [11, True], [13, True], [15, True], [16, True], [17, True], [5, False], [6, False], [7, False], [8, False]], [[2, True], [3, True], [5, True], [10, True], [11, True], [12, True], [13, True], [14, True]], [[2, True], [5, True], [8, True], [10, True], [11, True], [12, True], [13, True], [14, True]], [[1, True], [2, True], [5, True], [6, True], [7, True], [8, True], [11, True], [14, True], [3, False], [10, False], [12, False], [16, False], [17, False]], [[1, True], [7, True], [9, True], [13, True], [15, True], [16, True]], [[1, True], [2, True], [5, True], [10, True], [13, True], [14, True], [17, True], [3, False], [6, False], [11, False]], [[0, True], [5, True], [6, True], [7, True], [8, True], [11, True], [14, True], [17, True], [12, False], [16, False]], [[1, True], [3, True], [6, True], [7, True], [10, True], [11, True], [14, True], [5, False]], [[0, True], [5, True], [7, True], [8, True], [11, True], [12, True], [13, True], [14, True], [16, True], [2, False], [6, False]], [[2, True], [3, True], [4, True], [6, True], [11, True], [13, True], [16, True], [17, True], [1, False], [7, False], [8, False], [15, False]], [[1, True], [5, True], [6, True], [8, True], [10, True], [14, True], [17, True], [11, False]], [[1, True], [2, True], [5, True], [8, True], [10, True], [14, True], [17, True], [3, False], [6, False], [11, False]], [[2, True], [4, True], [6, True], [7, True], [11, True], [13, True], [15, True], [1, False], [16, False], [17, False]], [[1, True], [4, True], [11, True], [12, True], [14, True], [17, True], [2, False], [3, False], [13, False]], [[1, True], [2, True], [3, True], [5, True], [6, True], [7, True], [8, True], [12, True], [13, True], [14, True]], [[1, True], [2, True], [5, True], [6, True], [10, True], [12, True], [14, True], [17, True], [3, False], [7, False]], [[1, True], [6, True], [7, True], [9, True], [13, True], [14, True]], [[1, True], [9, True], [11, True], [12, True], [13, True], [15, True], [16, True], [17, True]], [[1, True], [7, True], [9, True], [12, True], [13, True], [14, True]], [[1, True], [2, True], [5, True], [10, True], [12, True], [14, True], [16, True]], [[3, True], [4, True], [6, True], [11, True], [12, True], [16, True], [0, False]], [[1, True], [8, True], [9, True], [12, True], [15, True], [16, True]], [[2, True], [4, True], [6, True], [7, True], [11, True], [13, True], [17, True], [15, False]], [[1, True], [2, True], [6, True], [7, True], [8, True], [10, True], [12, True], [14, True], [17, True]], [[2, True], [3, True], [4, True], [8, True], [11, True], [13, True], [14, True], [1, False], [12, False]], [[0, True], [4, True], [8, True], [13, True], [14, True], [2, False], [11, False], [12, False], [16, False], [17, False]], [[2, True], [3, True], [4, True], [8, True], [11, True], [15, True], [16, True], [1, False], [6, False], [7, False], [17, False]], [[2, True], [4, True], [8, True], [11, True], [12, True], [14, True], [0, False], [3, False]], [[1, True], [5, True], [6, True], [7, True], [11, True], [13, True], [14, True], [17, True], [0, False]], [[2, True], [3, True], [6, True], [8, True], [9, True], [13, True], [14, True]], [[1, True], [5, True], [6, True], [8, True], [10, True], [14, True], [16, True], [11, False]], [[1, True], [2, True], [6, True], [7, True], [10, True], [11, True], [14, True], [17, True]], [[1, True], [3, True], [6, True], [10, True], [11, True], [13, True], [14, True], [5, False]], [[1, True], [2, True], [3, True], [5, True], [7, True], [8, True], [11, True], [12, True], [14, True], [16, True]], [[2, True], [3, True], [4, True], [6, True], [11, True], [12, True], [15, True], [1, False], [7, False]], [[1, True], [2, True], [5, True], [10, True], [13, True], [14, True], [16, True], [6, False], [11, False]], [[1, True], [2, True], [3, True], [5, True], [6, True], [8, True], [11, True], [14, True], [16, True]], [[1, True], [2, True], [5, True], [6, True], [7, True], [8, True], [12, True], [13, True], [14, True], [17, True]], [[1, True], [3, True], [5, True], [6, True], [10, True], [13, True], [14, True], [17, True], [2, False], [11, False]], [[2, True], [3, True], [9, True], [11, True], [12, True], [13, True], [15, True], [16, True]], [[2, True], [3, True], [6, True], [7, True], [8, True], [9, True], [14, True], [17, True]], [[1, True], [8, True], [9, True], [11, True], [13, True], [14, True]], [[1, True], [2, True], [6, True], [7, True], [10, True], [11, True], [14, True], [16, True], [5, False], [13, False]], [[1, True], [3, True], [6, True], [10, True], [11, True], [12, True], [14, True], [17, True]], [[1, True], [2, True], [7, True], [8, True], [10, True], [11, True], [12, True], [13, True], [14, True]], [[0, True], [5, True], [6, True], [7, True], [11, True], [14, True], [16, True], [8, False], [12, False], [13, False]], [[1, True], [4, True], [7, True], [11, True], [15, True], [17, True], [2, False], [6, False]], [[0, True], [5, True], [7, True], [11, True], [12, True], [13, True], [14, True], [16, True], [17, True], [2, False], [6, False]], [[3, True], [4, True], [6, True], [7, True], [11, True], [13, True], [15, True], [0, False], [17, False]], [[1, True], [3, True], [5, True], [6, True], [11, True], [12, True], [13, True], [14, True], [16, True]], [[1, True], [3, True], [5, True], [10, True], [11, True], [13, True], [14, True], [17, True], [2, False], [6, False]], [[1, True], [6, True], [7, True], [10, True], [11, True], [12, True], [13, True], [14, True], [16, True]], [[1, True], [3, True], [4, True], [8, True], [12, True], [13, True], [15, True], [17, True], [2, False], [6, False], [7, False], [11, False]], [[4, True], [6, True], [8, True], [11, True], [12, True], [17, True], [0, False], [15, False]], [[1, True], [7, True], [8, True], [10, True], [11, True], [12, True], [15, True], [16, True], [17, True]], [[1, True], [5, True], [10, True], [12, True], [13, True], [14, True], [17, True]], [[1, True], [3, True], [6, True], [7, True], [8, True], [10, True], [12, True], [14, True]], [[0, True], [5, True], [6, True], [7, True], [12, True], [13, True], [14, True], [17, True], [3, False], [8, False], [11, False]], [[3, True], [4, True], [6, True], [7, True], [8, True], [11, True], [13, True], [16, True], [15, False]], [[2, True], [3, True], [4, True], [11, True], [12, True], [14, True], [0, False], [1, False], [8, False]], [[4, True], [6, True], [7, True], [8, True], [11, True], [13, True], [15, True], [1, False], [2, False], [3, False]], [[1, True], [2, True], [4, True], [11, True], [14, True], [3, False], [6, False], [10, False]], [[2, True], [3, True], [6, True], [7, True], [9, True], [12, True], [15, True], [16, True], [17, True]], [[1, True], [3, True], [4, True], [7, True], [11, True], [15, True], [16, True], [2, False], [6, False]], [[2, True], [3, True], [8, True], [9, True], [11, True], [13, True], [15, True], [16, True], [17, True]], [[2, True], [3, True], [6, True], [7, True], [10, True], [11, True], [12, True], [13, True], [14, True], [17, True]], [[1, True], [3, True], [4, True], [7, True], [12, True], [13, True], [15, True], [17, True], [6, False]], [[1, True], [4, True], [8, True], [11, True], [15, True], [16, True], [2, False], [13, False]], [[1, True], [6, True], [7, True], [8, True], [10, True], [12, True], [13, True], [14, True]], [[1, True], [2, True], [5, True], [7, True], [11, True], [12, True], [13, True], [14, True], [17, True]], [[2, True], [4, True], [6, True], [8, True], [11, True], [13, True], [17, True], [0, False], [15, False]], [[1, True], [2, True], [3, True], [5, True], [6, True], [7, True], [12, True], [13, True], [14, True], [17, True]], [[4, True], [5, True], [11, True], [13, True], [16, True], [1, False], [15, False]], [[1, True], [2, True], [4, True], [8, True], [12, True], [15, True], [17, True], [11, False]], [[2, True], [3, True], [4, True], [7, True], [11, True], [15, True], [17, True], [1, False], [6, False]], [[2, True], [4, True], [7, True], [11, True], [12, True], [14, True], [0, False], [3, False]], [[2, True], [3, True], [5, True], [6, True], [7, True], [11, True], [12, True], [14, True], [17, True], [0, False], [8, False], [16, False]], [[1, True], [3, True], [4, True], [11, True], [14, True], [2, False], [6, False], [16, False]], [[2, True], [3, True], [6, True], [7, True], [9, True], [13, True], [15, True], [16, True], [17, True]], [[2, True], [8, True], [9, True], [11, True], [14, True]], [[1, True], [2, True], [3, True], [4, True], [7, True], [15, True], [17, True], [6, False], [11, False]], [[1, True], [6, True], [7, True], [8, True], [10, True], [11, True], [14, True], [5, False]], [[1, True], [2, True], [3, True], [5, True], [6, True], [7, True], [12, True], [13, True], [14, True], [16, True]], [[1, True], [5, True], [6, True], [11, True], [12, True], [14, True], [17, True], [2, False]], [[2, True], [3, True], [4, True], [11, True], [13, True], [15, True], [16, True], [1, False]], [[2, True], [8, True], [9, True], [11, True], [12, True], [15, True], [16, True]], [[0, True], [5, True], [6, True], [7, True], [11, True], [13, True], [14, True], [17, True], [1, False], [2, False], [8, False], [12, False]], [[1, True], [2, True], [6, True], [10, True], [11, True], [12, True], [14, True], [17, True]], [[0, True], [4, True], [8, True], [13, True], [15, True], [17, True], [2, False], [3, False], [5, False], [6, False]], [[1, True], [3, True], [7, True], [8, True], [10, True], [11, True], [12, True], [14, True], [5, False], [6, False], [16, False]], [[1, True], [2, True], [3, True], [5, True], [8, True], [11, True], [12, True], [13, True], [14, True], [16, True]], [[1, True], [2, True], [3, True], [5, True], [6, True], [11, True], [13, True], [14, True], [16, True], [10, False]], [[1, True], [4, True], [8, True], [11, True], [15, True], [17, True], [2, False], [12, False]]]"
    logicstruct=literal_eval(logicstr)

которая даёт 2.8% ложно отрицательных и 0.6% ложно положительных ответов. Эти 134 группы обобщили около 700 положительных примеров. Всего было 1372 записи. Каждый запуск может генерировать новую структуру. Так же прошу заметить, что я не разделял данные на обучающею и проверочную выборки и критика в плане: «Да алгоритм просто всё запомнил и на новых данных накроется» — вполне уместна.

image

Автор: whileNotFalse

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js