Пока руководство Foxconn закупает металлических работников, суровые японские ученые готовят замену, видимо, старшим инженерам и маркетологам подобных компаний. Робот SOINN (что расшифровывается как self-organizing incremental neural network, то есть «самоорганизующаяся возрастающая нейронная сеть») способен учиться, анализировать окружающую обстановку и самостоятельно принимать решения. Получая новое задание, SOINN поступает точно также, как поступают лучшие представители человеческой расы: использует весь свой накопленный опыт, изучает окружающие его предметы и экспериментирует.
Фактически, перед тобой очень ранний прототип искусственного интеллекта. И, по всей видимости, в будущем мир изменится куда радикальней, чем это представляется большинству писателей-фантастов.
Робота научили решать задачи на основе базовых знаний
В Японии, в лаборатории Hasegawa Lab, учёные использовали «самоорганизующуюся инкрементную нейронную сеть» (Self-Organizing Incremental Neural Network — SOINN), для управления роботом HIRO (Kawada Industries).
SOINN(Self-Organizing Incremental Neural Network) — это неконтролируемый метод онлайн-обучения, способный к дополнительному обучению, основанному на Росте Нейронного Газа (Growing Neural Gas (GNG)) и самоорганизующихся картах (Self-Organizing Map (SOM)). Нестационарные онлайн-данные, имеют сложное распределение, но можно приблизительно распределить входные данные и оценить соответствующие число классов — это и является основой самоорганизации сети. Кроме того, сеть имеет следующие особенности: * отсутствие необходимости предопределения структуры сети, * высокая устойчивость к шумам. Т.о. методология SOINN может быть очень эффективна для использования в реальных приложениях.
Оперируя базовыми знаниями и навыками, робот «решает» как ему налить, а затем подать человеку стакан воды
5555
Пример работы самоорганизующейся инкрементной нейронной сети SOINN
Учёные использовали «самоорганизующуюся инкрементную нейронную сеть» (Self-Organizing Incremental Neural Network — SOINN), для управления роботом HIRO (Kawada Industries) с целью решения задач на основе базовых знаний ( т.е. алгоритм ИИ делает предположения и принимает решения на основе своего предыдущего опыта).
Новость как новость, обошла все околонаучные и IT-ные порталы, с пометкой про ещё один шаг в сторону разумных машин с искусственным интеллектом на борту.
И всё бы ничего — в новостных лентах часто появляются новости про очередные мега-достижения в науке и технике, но зайдя на сайт Hasegawa Lab можно обнаружить статьи в формате PDF про их исследования и, что ещё более важно, проект на C++: SOINN (C++) — Microsoft Visual Studio 2005 solution с пояснительной PDF-запиской How to Use the SOINN Software
Разумеется, автор сразу же скачал проект и попробовал его собрать. Как видно, проект создавался под Visual Studio 2005 и поэтому при попытке открыть его в более старших версиях, появляется мастер, предлагающий конвертацию, которая проходит без всяких проблем.
Если попробовать собрать проект под Visual Studio 2008 express edition, то сразу не получится — выпадает ошибка: fatal error RC1015: cannot open include file 'afxres.h'
— ошибка в отсутствии afxres.h (в файле Application\resource.rc)): Причина — нет MFC и чтобы решить эту проблему нужно просто отредактировать resource.rc и заменить «afxres.h» на <windows.h>
В стандартной сборке Visual Studio Express не хватает двух файлов winres.h и afxres.h. Их можно скачать и распаковать в C:\Program Files\Microsoft SDKs\Windows\v7.0A\Include — для экспресса 2010 или v6.0\Include — для 2008. Под обычной версией Visual Studio 2008 всё собирается с одного клика и в директории soinn-project-vs2005\Application\Release\ появляется файл Application.exe
Сразу же бросается в глаза, что код написан очень хорошо и понятно. Собственно сам SOINN содержится в одноимённом проекте и состоит всего из 6 файлов (soinn-project-vs2005\SOINN\): CEdge.cpp CEdge.h CNode.cpp CNode.h CSOINN.cpp CSOINN.h CSOINN — реализует алгоритм SOINN CNode — узел (нейрон) в сети CEdge — связь между двумя узлами
SOINN API:
CSOINN::InputSignal(const double *signal) — реализует алгоритм SOINN для входных данных (signal).
CSOINN::Classify(void) — присваивает идентификатор класса для всех узлов в соответствии с текущей структурой сети.
CSOINN::Reset(const int dimension=NO_CHANGE, const int lambda=NO_CHANGE, const int ageMax=NO_CHANGE)
— сбрасывает параметры SOINN (размер, счётчики удаления узлов). В текущей реализации, все узлы и связи сети, также удаляются.
CSOINN::GetNodeNum(const bool ignoreAcnode=false) — возвращает текущее количество узлов в сети.
CSOINN::GetClassNum() — возвращает текущее число классов в сети.
CSOINN::GetNode(const int node) — возвращает экземпляр CNode с id, переданным в качестве аргумента функции. CSOINN::GetClassFromNode(const int node) — возвращает идентификатор класса из CNode с id, переданным в качестве аргумента функции.
CSOINN::SaveNetworkData(const char *fileName) — сохраняет текущее состояние сети в заданный файл.
CSOINN::LoadNetworkData(const char *fileName) — загружает сеть из заданного файла. CNode::GetSignal() — возвращает вес вектора экземпляра CNode.
CNode::GetClass() — возвращает идентификатор класса экземпляра CNode.
при добавлении нового сигнала через CSOINN::InputSignal(const double *signal) проверяется существующее количество узлов в сети и если их меньше двух, то сигнал просто добавляется как новый узел сети, во всех остальных случаях — сначала выявляются два узла сети, ближайших к поступающему сигналу — метод CSOINN::FindWinnerAndSecondWinner(int &winner, int &secondWinner, const double *signal) Затем, производится проверка относится ли сигнал к новым данным с помощью метода CSOINN::IsWithinThreshold(const int winner, const int secondWinner, const double *signal) — внутри него просто вычисляется расстояние от двух ближайших узлов до заданного сигнала и если оно больше, чем порог подобия ( CSOINN::GetSimilarityThreshold(const int node) ), то сигнал добавляется в качестве нового узла сети, в противном случае формируется связь между двумя полученными узлами.
классификация — CSOINN::Classify(void) — это рекурсивный обход сети и назначение id-ка класса (CSOINN::SetClassID(const int node, const int classID)) всем узлам, которые связанны между собой.
Попробуем запустить этот алгоритм :)
создадим проект, в настройках проекта укажем: С/C++ - Code Generation - Runtime Library - /MT в противном случае при сборке проекта появится ошибка:
LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library
пропишем путь до заголовочных файлов и путь до готового SOINN.lib
мой тестовый проект на основе примера из руководства:
// // тестовый пример работы с SOINN // http://haselab.info/soinn-e.html // // http://robocraft.ru //
#include <stdio.h> #include <stdlib.h> #include <math.h>
#include "CSOINN.h"
#define DIMENSION 2 #define REMOVE_NODE_TIME 200 #define DEAD_AGE 100
// показать информацию о SOINN-сети void show_SOINN_info(CSOINN* pSOINN);
// расстояние между двумя сигналами double Distance(const double *signal1, const double *signal2, int dimension=DIMENSION);
int main(int argc, char* argv[]) { printf("[i] Start...\n");
CSOINN* pSOINN = 0;
// создание объекта SOINN pSOINN = new CSOINN ( DIMENSION , REMOVE_NODE_TIME , DEAD_AGE );
if(!pSOINN){ printf("[!] Error: cant allocate memory!\n"); return -1; }
// для хранения входных данных double signal[DIMENSION];
int i=0;
//----------------------------------------- for(i=0; i<10; i++){ signal[0] = 0; signal[1] = i;
// добавляем данные в сеть pSOINN -> InputSignal ( signal ); }
// классификация данных pSOINN -> Classify ();
// поcмотрим информацию о SOINN-сети show_SOINN_info(pSOINN); //----------------------------------------- double delta = 0.9;
#if 1 for(i=0; i<10; i++){ signal[0] = i; signal[1] = i;
// добавляем данные в сеть pSOINN -> InputSignal ( signal ); }
// классификация данных pSOINN -> Classify ();
// поcмотрим информацию о SOINN-сети show_SOINN_info(pSOINN); #endif //-----------------------------------------
// 1-NN algorithm . double minDist = CSOINN :: INFINITY ; int nearestID = CSOINN :: NOT_FOUND ;
// тестовый сигнал double targetSignal[DIMENSION]; targetSignal[0] = 1+delta; targetSignal[1] = 1+delta;
for(i=0; i < pSOINN->GetNodeNum(); i++ ){
double* nodeSignal = pSOINN -> GetNode ( i )-> GetSignal (); double dist = Distance ( targetSignal , nodeSignal );
if ( minDist > dist ) { minDist = dist ; nearestID = i; } }
int nearestClassID = pSOINN -> GetNode ( nearestID )-> GetClass (); printf ("[i] SOINN: Nearest Node ID : %d, Class ID : %d.\n", nearestID , nearestClassID );
if(pSOINN){ delete pSOINN; pSOINN = 0; }
printf("[i] End.\n"); return 0; }
// показать информацию о SOINN-сети void show_SOINN_info(CSOINN* pSOINN) { if(!pSOINN){ return; }
// покажем информацию о сети printf("[ ] SOINN info: \n"); printf("[i] Dimension: %d\n", pSOINN->GetDimension()); printf("[i] NodeNum: %d\n", pSOINN->GetNodeNum()); printf("[i] EdgeNum: %d\n", pSOINN->GetEdgeNum()); printf("[i] ClassNum: %d\n", pSOINN->GetClassNum()); printf("----------------------------\n");
}
// расстояние между двумя сигналами double Distance(const double *signal1, const double *signal2, int dimension) { int i; double sum;
if (signal1 == NULL || signal2 == NULL || dimension<=0) return 0.0; sum = 0.0; for (i=0; i<dimension; i++){ sum += (signal1[i]-signal2[i])*(signal1[i]-signal2[i]); } return sqrt(sum)/(double)dimension; }
вывод:
[i] Start... [ ] SOINN info: [i] Dimension: 2 [i] NodeNum: 10 [i] EdgeNum: 0 [i] ClassNum: 10 ---------------------------- [ ] SOINN info: [i] Dimension: 2 [i] NodeNum: 19 [i] EdgeNum: 1 [i] ClassNum: 18 ---------------------------- [i] SOINN: Nearest Node ID : 11, Class ID : 10. [i] End.
угу — как-то работает :)
Попробуем как-нибудь применить эту сетку :) Например, подружим её с OpenCV :)
в качестве сигнала я использую координаты ненулевых пикселей бинарной картинки (в данном примере — это границы, полученные после детектора границ Кенни).
при попытке её приладить возникли трудности с доступом к переменным класса CEdge и CNode, которые в программе японцев решается friend-доступом, но я решил пока просто перетащить исходники SOINN в свой проект и дать public-доступ закрытым переменным (m_from и m_to из CEdge, m_signal из CNode), которые нужны для отрисовки :(
На одной картинке мы не увидим почти ничего интересного, но если обернуть процедуру обработки сигнала в цикл, то всё будет намного интереснее (и это не удивительно — жизнь это движение ;)
основа: CSOINN* pSOINN = 0;
// создание объекта SOINN pSOINN = new CSOINN ( DIMENSION , REMOVE_NODE_TIME , DEAD_AGE );
if(!pSOINN){ printf("[!] Error: cant allocate memory!\n"); return -1; }
unsigned int round = 0;
while(cvWaitKey(33)!=27){ // для хранения входных данных double signal[DIMENSION];
// пробегаемся по всем пикселям изображения for( int y=0; y<bin->height; y++ ) { uchar* ptr = (uchar*)(bin->imageData + y * bin->widthStep); for( int x=0; x<bin->width; x++ ) { // если пиксель белый if(ptr[x]>0){ signal[0] = x; signal[1] = y; // добавляем данные в сеть pSOINN -> InputSignal ( signal ); } } }
// классификация данных pSOINN -> Classify ();
// покажем номер итерации printf("[i] round: %d \n", round++); // поcмотрим информацию о SOINN-сети show_SOINN_info(pSOINN);
show_SOINN(pSOINN, soinn);
cvShowImage("SOINN", soinn);
} функция отображения сети (адаптированый вариант функции COutputWindow::Refresh()):
// показать сеть SOINN на картинке :) void show_SOINN(CSOINN* pSOINN, IplImage* img) { if(!pSOINN || !img){ return; }
int i, f, t, nodeNum, edgeNum; double x, y, x0, y0, x1, y1; CNode* node; CEdge* edge;
// очистим картинку cvZero(img);
edgeNum = pSOINN->GetEdgeNum(); for (i=0; i<edgeNum; i++){ edge = pSOINN->GetEdge(i); f = edge->m_from; node = pSOINN->GetNode(f); x0 = node->m_signal[0]; y0 = node->m_signal[1];
t = edge->m_to; node = pSOINN->GetNode(t); x1 = node->m_signal[0]; y1 = node->m_signal[1];
cvLine(img, cvPoint((int)x0, (int)y0), cvPoint((int)x1, (int)y1), CV_RGB(250,250,250), 1, 8); }
nodeNum = pSOINN->GetNodeNum(); for (i=0; i<nodeNum; i++){ node = pSOINN->GetNode(i); x = node->m_signal[0]; y = node->m_signal[1];
cvCircle(img,cvPoint((int)x, (int)y), 2, m_color[node->m_classID%MAX_COLOR], -1, 8); } }
1-й раунд:
132-й:
а вот какую gif-ку (3.3Mb) у меня получилось замутить:
забавно — прямо возникло чувство сопричастности к передовым разработкам в области ИИ для роботов :)))
SOINN.zip — архив проектов (с exe-ками): SOINN\soinn-project-vs2005 SOINN\test SOINN\soinn2cv
UPD добавил в архив проект SOINN\soinn2cv2, в котором в качестве входного сигнала в сеть подаются координаты клика мышкой
обратите внимание, что для программы soinn2cv требуется OpenCV версии 2.1
Оригинал статьи
|