Список работ

Самоорганизующаяся карта Кохонена в задаче идентификации серийного номера

Содержание

Постановка задачи

Задача идентификации серийного номера (СН) - это задача определения номенклатурной группы (НГ) изделия по его СН. Информация об изделиях (номенклатуре), их номенклатурных группах и серийных номерах хранится в базе серийных номеров и частично дублируется в базе гарантийного отдела. Обе базы созданы на платформе 1С:Предриятие 8.
Для определения номенклатурной группы изделия по его СН предварительно создается и обучается самоорганизующаяся карта Кохонена (СКК).
При приеме изделия на гарантийное обслуживание по СН изделия определяется узел СКК, отвечающий СН, а затем и НГ, ассоциируемая с найденным узлом.
Таким образом, решаются следующие задачи:

  1. Создание и обучение СКК.
  2. Ассоциирование номенклатурных групп с узлами СКК.
  3. Создание программы, определяющей по СН ближайший узел СКК и, следовательно, НГ изделия.

СКК создается средствами 1С:Предриятие 8, а графическое представление СКК выполняется в 3ds Max средствами MAXScript.

Самоорганизующаяся карта Кохонена

Самоорганизующаяся карта Кохонена - это вид нейронной сети, каждый узел (нейрон) которой связан с каждым элементом входного слоя (на рис. 1 входной слой показан зеленым цветом).

Структура карты Кохонена

Рис. 1. Структура двухмерной 16-узловой СКК

Размерность СКК определяется числом измерений пространства, в котором располагаются узлы СКК.
Другим параметром СКК является ее число узлов.
Мы будем использовать двухмерную СКК с числом узлов, равным квадрату целого числа, которое будем называть размером СКК (рСКК).
Для наглядности будем отображать СКК на регулярную квадратную решетку с числом узлов рСКК * рСКК.
Связь узла СКК с входным слоем фиксируется вектором весовых коэффициентов (далее узловым вектором, УВ). Размер УВ равен числу элементов во входном слое.
Обучение СКК заключается в определение весовых коэффициентов УВ, таких, что схожим объектам отвечают компактно сгруппированные узлы СКК. Эти группы узлов СКК не пересекаются и различаются некоторыми характеристиками.
Так, при работе с СН каждая группа СКК отвечает некоторой НГ.
По своей сути СКК проецирует многомерное пространство (его размерность определяет число элементов входного слоя СКК) в пространство с более низкой размерностью.
Подавая на вход обученной СКК описание объекта, можно определить узел, наиболее полно отвечающий этому описанию. В случае СН после определения такого узла мы легко найдем и соответствующую НГ, то есть решим поставленную задачу.
Наглядно, например, 36-узловую сеть (рСКК = 6) можно представить в виде растровой карты (рис. 2), в которой узлы одной группы закрашиваются оттенками одного цвета.

Карта Кохонена с квадратными узлами

Рис. 2. Представление двухмерной 36-узловой СКК в виде растровой карты

Такое представление получается непринужденно, если размер УВ равен 3. В этом случае каждый узел закрашивается цветом, RGB-компоненты которого определяются весовыми коэффициентами УВ.

Для получения рис. 1 в 3ds Max можно использовать следующий MAXScript-код (берется вид сверху):

delete $*
fn drawLine ss k p1 p2 = (
 addNewSpline ss
 addKnot ss k #corner #line p1
 addKnot ss k #corner #line p2
 updateShape ss
)
r = 6
n = 4
d = 3 * r
x = -0.5 * d * (n - 1)
y0 = -r * n + 0.5 * r
for k = 1 to n do (
 y = y0
 for m = 1 to n do (
  p = [x, y, 0]
  sphere radius:r pos:p wireColor:red
  y += d
 )
 x += d
)
n2 = 4
r2 = 0.5 * r
d2 = 5 * r2
x2 = -0.5 * d2 * (n2 - 1)
y2 = y0 - 7 * r2
for k2 = 1 to n2 do (
 p2 = [x2, y2, 0]
 sphere radius:r2 pos:p2 wireColor:green
 x2 += d2
 x = -0.5 * d * (n - 1)
 for k = 1 to n do (
  y = y0
  for m = 1 to n do (
   ss = line render_renderable:on render_displayRenderMesh:on wireColor:black\
    render_useViewportSettings:off render_displayRenderSettings:true render_thickness:0.5
   p = [x, y, 0]
   drawLine ss 1 p2 p
   y += d
  )
  x += d
 )
)

Рис. 2 поможет получить нижеприводимый MAXScript-код:

delete $*
d = 10
n = 6
x = -0.5 * d * (n - 1)
y0 = x
l = 0
n2 = n * n / 2
for k = 1 to n do (
 y = y0
 for m = 1 to n do (
  p = [x, y, 0]
  l += 1
  clr = if l < n2 then random [0, 0, 0] [255, 255, 0] else random [0, 0, 0] [0, 255, 255]
  box width:d length:d height:(random 1 5) pos:p wireColor:clr
  y += d
 )
 x += d
)

Замечание. Вместо Box можно употребить Plane.

Изделие, серийные номера и входной вектор

В базе гарантийного отдела изделие описывается в справочнике Номенклатура. Каждое реализованное изделие снабжается СН, который фиксируется на изделии в виде штрих-кодовой наклейки. Число СН изделия неограниченно.
СН хранится в справочнике серийных номеров, который подчинен справочнику Номенклатура. Кроме того, каждое изделие входит в одну НГ, что отражается в справочнике номенклатурных групп.
СН - это последовательность из не более 30 символов без ведущих пробелов, например (в скобках указана НГ, в которую входит изделие с текущим СН):

000-0009861 (DVD RW)
000001 09287131 (UPS)
9YP09DRB (Hard Drives)
A-204-G03-01679 (Media)
CDDHZBED (Hard Drives)
EW7722UTN15CB00101 (Netware)
IG31AM4S-03 70008G11403005 (Mother Boards)
S/N:17000114600372 (Cases)
U100-047RUK0908000322 (Platforms)
VN700M1W2N-A1112000843 (Cases)
YTD6TSAA42416D00211 (Cases)

В нашем случае на вход СКК подается информация о СН, представленная в виде вектора из трех элементов.
Входной вектор получается по следующей схеме:

Результат обеспечивает следующая 1С-функция, получающая на входе СН:

функция создатьВВ(снТ)
 // Массив хранит компоненты ВВ и код НГ верхнего уровня
 вв = новый массив(4);
 // Простое число - делитель числового представления элемента входного вектора
 пЧ = 109;
 дВ = стрДлина(снТ) / 3;
 д = цел(дВ);
 если д < дВ тогда д = д + 1 конецЕсли;
 н = 1;
 для к = 0 по 2 цикл
  вв[к] = сред(снТ, н, д);
  н = н + д;
 конецЦикла;
 // Дополняем при необходимости последний элемент вектора хвостовыми пробелами
 пока стрДлина(вв[2]) < д цикл вв[2] = вв[2] + " " конецЦикла;
 для к = 0 по 2 цикл
  в = "";
  для к2 = 1 по д цикл в = в + кодСимвола(сред(вв[к], к2, 1)) конецЦикла;
  вв[к] = число(в) % пЧ;
 конецЦикла;
 возврат вв;
конецФункции

Вызов функции и печать результата:

снТ = "9YP09DRB";
вв = создатьВВ(снТ);
для к = 0 по 2 цикл сообщить(вв[к]) конецЦикла;

Напечатает:
81
64
85

При работе с СН "9YP09DRB" получаем следующие три элемента: "9YP", "09D" и "RB " (последний элемент дополнен хвостовым пробелом).
Далее после замены символов на Unicode-коды имеем следующие три элемента: "578980", "485768" и "826632".
В качестве результата берем остаток от деления элемента на заданное простое число (в примере оно равно 109).

Обучающая выборка

В тестовом примере задействованы шесть НГ верхнего уровня и все их дочерние НГ (для них в справочнике НГ значение реквизита Кластер = истина, рис. 3).

Кластерный элемент справочника Номенклатурные группы

Рис. 3. Кластерная НГ

Кластерные НГ верхнего уровня (в скобках указан код НГ):

CD (263);
Coolers (1218).
Hard Drives (219);
Mother Boards (226);
UPS (387);
Video Cards (297)

Флажок Кластер устанавливается для шести НГ верхнего уровня интерактивно. В дочерних НГ этот флажок устанавливается следующим кодом:

пст = справочники.НоменклатурныеГруппы.ПустаяСсылка();
змн = истина;
пока змн цикл
 змн = ложь;
 в = справочники.НоменклатурныеГруппы.Выбрать();
 пока в.Следующий() цикл
  если в.Кластер тогда продолжить конецЕсли;
  р = в.Родитель;
  если р = пст тогда продолжить конецЕсли;
  если р.Кластер тогда
   во = в.ПолучитьОбъект();
   во.Кластер = истина;
   во.Записать();
   змн = истина;
  конецЕсли;
 конецЦикла;
конецЦикла;

СН попадает в тестовую выборку, если его владелец (изделие) входит в тестовую (кластерную) НГ:

функция обучающаяВыборка()
 з = новый запрос;
 з.текст = "выбрать код из справочник.СерийныеНомера
 | где владелец.НоменклатурнаяГруппа.Кластер и владелец.код <> код";
 тз = з.Выполнить().Выгрузить();
 тз.Колонки.Добавить("вв");
 // Формируем входные векторы; код функции создатьВВ см. выше
 для каждого с из тз цикл с.вв = создатьВВ(сокрЛП(с.код)) конецЦикла;
 возврат тз;
конецФункции

Из обучающей выборки исключены фиктивные СН. Код такого СН равно коду владельца.

Вызов функции и печать одного ВВ.

тз = обучающаяВыборка();
вв = тз[300][1];
для к = 0 по 2 цикл сообщить(вв[к]) конецЦикла;

Обучающая выборка малая

При описанном выше способе формирования обучающей выборки (ОВ) в нее попадет несколько десятков миллионов СН. Такой размер ОВ чрезмерен. Для его уменьшения можно включать в ОВ лишь часть СН каждой номенклатуры, что и делает приводимая ниже функция:

функция обучающаяВыборкаМалая()
 пст = справочники.НоменклатурныеГруппы.ПустаяСсылка();
 т = новый текстовыйДокумент;
 ф = "c:\td.txt";
 тзО = новый таблицаЗначений;
 тзО.Колонки.Добавить("вв");
 з = новый запрос;
 з.текст = "выбрать ссылка, номенклатурнаяГруппа как нг из справочник.Номенклатура
 | где НоменклатурнаяГруппа.Кластер";
 в = з.Выполнить().Выбрать();
 з.текст = "выбрать первые 5 код из справочник.СерийныеНомера
 | где владелец = &влд и владелец.код <> код";
 пока в.Следующий() цикл
  з.УстановитьПараметр("влд", в.Ссылка);
  в2 = з.Выполнить().Выбрать();
  // Найдем код НГ верхнего уровня (код функции найтиНГ0 см. ниже)
  кодНГ0 = найтиНГ0(в.нг, пст);
  // Формируем входные векторы; код функции создатьВВ см. выше
  пока в2.Следующий() цикл
   с = тзО.Добавить();
   вв = создатьВВ(сокрЛП(в2.код));
   вв[3] = кодНГ0;
   с.вв = вв;
   т.ДобавитьСтроку("" + вв[0] + ", " + вв[1] + ", " + вв[2] + ", " + кодНГ0);
  конецЦикла;
 конецЦикла;
 т.Записать(ф);
 возврат тзО;
конецФункции

функция найтиНГ0(знач нг, пст)
 пока истина цикл
  р = нг.Родитель;
  если р = пст тогда прервать конецЕсли;
  нг = р;
 конецЦикла;
 возврат нг.код;
конецФункции

Вызов функции обучающаяВыборкаМалая и печать одного ВВ и кода НГ верхнего уровня.

тзО = обучающаяВыборкаМалая();
вв = тзО[200][0];
сообщить("" + вв[0] + ", " + вв[1] + ", " + вв[2] + ", " + вв[3]);

Программа обучения СКК берет данные из файла c:\td.txt, созданного функцией обучающаяВыборкаМалая.

Обучение карты Кохонена

Алгоритм обучения карты Кохонена

Входные данные: обучающая выборка.

Выходные данные: веса узлов СКК (записываются в таблицу значений тзСКК).

  1. Начало.
  2. Инициализировать УВ каждого узла.
  3. Выбрать случайным образом элемент Э из обучающего набора данных.
  4. Определить узел У (лучший узел), наибольшим образом отвечающий выбранному обучающему элементу Э.
  5. Вычислить радиус области О, в которой расположены соседи узла У.
  6. Пересчитать векторы весов всех узлов, принадлежащих области О, с целью их приближения к элементу Э (обучение).
  7. Повторить шаги 3-6 заданное число раз.
  8. Останов.

Инициализация узловых векторов

Инициализация всех УВ обеспечивает следующая функция:

функция начальныеУВ(рСКК, чВ)
 // рСКК - размер СКК (число узлов в СКК равно рСКК * рСКК)
 // чВ - размер входного вектора (число элементов во входном слое)
 гсч = новый генераторСлучайныхЧисел(1);
 // Храним УВ в таблице значений размера рСКК * рСКК
 // Последний элемент УВ резервируем под код НГ высшего уровня, ассоциируемой с узлом СКК
 тзСКК = новый таблицаЗначений;
 для к = 0 по рСКК - 1 цикл тзСКК.Колонки.Добавить("к" + к) конецЦикла;
 для к = 0 по рСКК - 1 цикл
  с = тзСКК.Добавить();
  для к2 = 0 по рСКК - 1 цикл
   ув = новый массив(чВ + 1);
   для к3 = 0 по чВ цикл ув[к3] = гсч.СлучайноеЧисло(0, 255) конецЦикла;
   с[к2] = ув;
  конецЦикла;
 конецЦикла;
 возврат тзСКК;
конецФункции

Вызов функции и проверка результата:

// Размер СКК (число узлов в СКК равно рСКК * рСКК)
рСКК = 40;
// Размер входного вектора (число элементов во входном слое)
чВ = 3;
тзСКК = начальныеУВ(рСКК, чВ);
// Вывод одного случайно выбранного узлового вектора
гсч = новый генераторСлучайныхЧисел(3);
х = гсч.СлучайноеЧисло(0, рСКК);
у = гсч.СлучайноеЧисло(0, рСКК);
вв = тзСКК[х][у];
сообщить("ВВ: " + вв[0] + ", " + вв[1] + ", " + вв[2]);

Напечатает
ВВ: 42, 175, 67

Определение лучшего узла

Для текущего ВВ определяется лучший узел (ЛУ). Расстояние между ВВ и УВ определяется по евклидовой метрике:

Формула вычисления расстояния при обучении карты Кохонена

Для ЛУ расстояние между его УВ и ВВ минимально.
Замечание. Для сокращения счета квадратный корень не извлекается.

Массив, содержащий индексы ЛУ в СКК, возвращается следующей функцией:

функция найтиЛУ(тзСКК, вв)
 лу = новый массив(2);
 дЛУ = 3 * 255 * 255;
 лу[0] = -1;
 лу[1] = -1;
 рСКК = тзСКК.количество() - 1;
 рВВ = вв.количество() - 2;
 для к0 = 0 по рСКК цикл
  с = тзСКК[к0];
  для к1 = 0 по рСКК цикл
   ув = с[к1];
   д = 0;
   для к2 = 0 по рВВ цикл
    р = вв[к2] - ув[к2];
    д = д + р * р;
   конецЦикла;
   если д < дЛУ тогда
    дЛУ = д;
    лу[0] = к0;
    лу[1] = к1;
   конецЕсли;
  конецЦикла;
 конецЦикла;
 возврат лу;
конецФункции

Вызов функции и печать результата:

гсч = новый генераторСлучайныхЧисел(5);
тзО = обучающаяВыборкаМалая();
// Инициализации СКК
тзСКК = начальныеУВ(40, 3);
// Случайный выбор ВВ
кО = тзО.количество() - 1;
вв = тзО[гсч.СлучайноеЧисло(0, кО)][0];
лу = найтиЛУ(тзСКК, вв);
сообщить("Индексы ЛУ: " + лу[0] + " " + лу[1]);

Определение соседей лучшего узла

Соседи ЛУ - это узлы, расположенные в окрестности ЛУ, то есть в пределах окружности радиуса рЛУ с центром в ЛУ.
Радиус рЛУ уменьшается с каждой итерацией алгоритма обучения по следующей зависимости:

рЛУ = рСКК * exp(-чои / (чои - к + 1)),

где
рСКК - размер СКК (так, в случае 36-узловой СКК рСКК = 6);
чои - число итераций алгоритма обучения;
к - номер итерации;
рЛУ - текущее значение радиуса окрестности ЛУ.

Поскольку СКК отображается на регулярную квадратную решетку, то для вычисления соседей ЛУ подойдет следующая функция:

функция соседиЛУ(рСКК, лу, рЛУ)
 // Таблица значений, хранящая массивы с координатами соседей ЛУ
 // Также содержит и массив с координатами самого ЛУ
 тзСЛУ = новый таблицаЗначений;
 тзСЛУ.колонки.добавить("сЛУ");
 лу0 = лу[0];
 лу1 = лу[1];
 рЦ = цел(рЛУ);
 если рЦ = 0 тогда
  // В результирующей таблице значений только сам ЛУ
  м = новый массив(3);
  м[0] = лу0; м[1] = лу1; м[2] = 0;
  с = тзСЛУ.добавить();
  с.сЛУ = м
 иначе
  рЛУ2 = рЛУ * рЛУ;
  // Границы минимальной области, охватывающей окрестность ЛУ
  // Левая, правая, нижняя и верхняя границы охватывающей области
  л = лу0 - рЦ; п = лу0 + рЦ;
  н = лу1 - рЦ; в = лу1 + рЦ;
  для х = л по п цикл
   // Область может выходит за пределы решетки СКК
   если х < 0 или х > рСКК - 1 тогда продолжить конецЕсли;
   х2 = ?(х < 0, -х, х) - лу0;
   х2 = х2 * х2;
   для у = н по в цикл
    если у < 0 или у > рСКК - 1 тогда продолжить конецЕсли;
    у2 = ?(у < 0, -у, у) - лу1;
    д2 = х2 + у2 * у2;
    если д2 <= рЛУ2 тогда
     м = новый массив(3);
     м[0] = х; м[1] = у; м[2] = д2;
     с = тзСЛУ.добавить();
     с.сЛУ = м
    конецЕсли;
   конецЦикла;
  конецЦикла;
 конецЕсли;
 возврат тзСЛУ;
конецФункции

Проверка функции:

рСКК = 40;
рЛУ = 5;
лу = новый массив(2);
лу[0] = 5;
лу[1] = 15;
тзСЛУ = соседиЛУ(рСКК, лу, рЛУ);
сообщить("Соседи ЛУ и квадрат расстояния соседа от ЛУ:");
для каждого с из тзСЛУ цикл сообщить("" + с.сЛУ[0] + ", " + с.сЛУ[1] + ", " + с.сЛУ[2]) конецЦикла;

Напечатает:
Соседи ЛУ и квадрат расстояния соседа от ЛУ:
0, 15, 5
1, 12, 5
...

Пересчет узловых векторов

После определения соседей ЛУ пересчитываются веса их УВ и вес УВ самого ЛУ по следующей формуле:

УВik = УВik - 1 + пК * (ВВik - 1 - УВik - 1),

где
k - номер итерации;
ВВ - входной вектор;
УВ - узловой вектор;
пК - поправочный коэффициент.

пК= рЛУ / рСКК * (1 - д / рСКК),

где
рСКК - размер СКК;
рЛУ - текущее значение радиуса окрестности ЛУ;
д - расстояние соседа от ЛУ.

Поправочный коэффициент снижается с каждой обучающей итерацией, поскольку зависит от радиуса окрестности ЛУ. Также пК уменьшается и при удалении узла от ЛУ.

Пересчет и изменение кода НГ, ассоциируемой с узлом, обеспечивает следующая процедура:

процедура пересчетУВ(тзСЛУ, вв, рЛУ, рСКК, тзСКК)
 рВ = вв.количество() - 2;
 пК1 = рЛУ / рСКК;
 для каждого с из тзСЛУ цикл
  х = с.сЛУ[0];
  у = с.сЛУ[1];
  пК2 = (1.0 - с.сЛУ[2] / рСКК);
  ув = тзСКК[х][у];
  для i = 0 по рВ цикл
   ув[i] = ув[i] + пК1 * пК1 * (вв[i] - ув[i]);
  конецЦикла;
  ув[рВ + 1] = вв[рВ + 1];
  тзСКК[х][у] = ув;
 конецЦикла;
конецПроцедуры

// Возвращает массив с компонентами ВВ, считанного из текста тВВ
функция изСтрокиВВ(гсч, тВВ)
 сч = гсч.СлучайноеЧисло(1, тВВ.КоличествоСтрок());
 с = тВВ.ПолучитьСтроку(сч);
 вв = новый массив(4);
 п = найти(с, ",");
 вв[0] = число(лев(с, п - 1)); с = сред(с, п + 2); п = найти(с, ",");
 вв[1] = число(лев(с, п - 1)); с = сред(с, п + 2); п = найти(с, ",");
 вв[2] = число(лев(с, п - 1));
 вв[3] = число(сред(с, п + 2));
 возврат вв;
конецФункции

Вызов процедуры ПересчетУВ и печать ВВ, УВ лучшего узла до и после пересчета:

гсч = новый генераторСлучайныхЧисел(7);
тВВ = новый текстовыйДокумент;
// Читаем данные из файла с обучающими ВВ
тВВ.Прочитать("c:\td.txt");
// Получаем случайный ВВ (текст функции ИзСтрокиВВ см. чуть выше)
вв = изСтрокиВВ(гсч, тВВ);
сообщить("ВВ:");
сообщить("" + вв[0] + " " + вв[1] + " " + вв[2] + " " + вв[3]);
// Размер СКК (число узлов в СКК равно рСКК * рСКК)
рСКК = 40;
// Размер входного вектора (число элементов во входном слое)
чВ = 3;
тзСКК = начальныеУВ(рСКК, чВ);
рЛУ = 5;
лу = новый массив(2);
лу[0] = 5;
лу[1] = 15;
тзСЛУ = соседиЛУ(рСКК, лу, рЛУ);
ув = тзСКК[лу[0]][лу[1]];
сообщить("УВ до:");
сообщить("" + ув[0] + " " + ув[1] + " " + ув[2] + " " + ув[3]);
пересчетУВ(тзСЛУ, вв, рЛУ, рСКК, тзСКК);
сообщить("УВ после:");
сообщить("" + ув[0] + " " + ув[1] + " " + ув[2] + " " + ув[3]);

Напечатает:
ВВ:
107 35 77 226
УВ до:
115 251 115 149
УВ после:
114,875 247,625 114,40625 226

Программа обучения карты Кохонена

Приводимая программа обучения СКК вызывает ранее описанные функции и процедуру. Результирующие УВ, хранимые таблицей значений тзСКК, выгружаются в текстовый файл c:\sm.txt.

// Размер СКК (число узлов в СКК равно рСКК * рСКК)
рСКК = 40;
// Размер входного вектора (число элементов во входном слое)
чВ = 3;
// Число обучающих итераций
чои = 1000;
// Имя файла с ВВ обучающей выборки
фов = "c:\td.txt";
// Имя файла с результирующими УВ
фув = "c:\sm.txt";
// Начальное значения радиуса в задаче поиска соседей ЛУ
рЛУ0 = рСКК / 2;
// ГСЧ для случайного выбора обучающего ВВ
гсч = новый генераторСлучайныхЧисел(7);
// Текст с обучающими ВВ (каждая строка текста содержит компоненты одного ВВ)
тВВ = новый текстовыйДокумент;
// Читаем данные из файла с обучающими ВВ
тВВ.Прочитать("c:\td.txt");
// Инициализация СКК (задание начальных весов УВ)
тзСКК = начальныеУВ(рСКК, чВ);
// Обучение
тН = текущаяДата();
для к = 1 по чои цикл
 // Получаем обучающий вектор
 вв = изСтрокиВВ(гсч, тВВ);
 // Находим для него ЛУ
 лу = найтиЛУ(тзСКК, вв);
 // Радиус для поиска соседей ЛУ
 рЛУ = окр(рСКК * exp(-чои / (чои - к + 1)), 2);
 сообщить("к = " + к + "; рЛУ = " + рЛУ);
 // Находим соседей ЛУ
 тзСЛУ = соседиЛУ(рСКК, лу, рЛУ);
 // Пересчет весовых коэффициентов соседей ЛУ и самого ЛУ
 пересчетУВ(тзСЛУ, вв, рЛУ, рСКК, тзСКК);
конецЦикла;
сообщить("Время обучения (с): " + (текущаяДата() - тН));
// Вывод в файл результирующих УВ и кода ассоциируемого с узлом НГ. Используем имеющийся объект тВВ
тВВ.Очистить();
для каждого с из тзСКК цикл
 для к = 0 по рСКК - 1 цикл
  ув = с[к];
  фС = "ЧЦ=10; ЧДЦ=2; ЧРД='.'; ЧН=";
  ув0 = сокрЛ(формат(ув[0], фС));
  ув1 = сокрЛ(формат(ув[1], фС));
  ув2 = сокрЛ(формат(ув[2], фС));
  тВВ.ДобавитьСтроку("" + ув0 + ", " + ув1 + ", " + ув2 + ", " + ув[3]);
 конецЦикла;
конецЦикла;
тВВ.Записать(фув, кодировкаТекста.ANSI);

Визуализация карты Кохонена

Выполнена в 3ds Max при помощи следующего кода:

delete $*
nSM = 40
w = 4
xyShft = -w * nSM / 2
// Открываем файл с УВ и кодами НГ, ассоциируемыми с узлами СКК
f = openFile "c:\\sm.txt"
for x = 1 to nSM do (
 xP = xyShft + w * x
 for y = 1 to nSM do (
  yP = xyShft + w * y
  nV = readLine f
  arrNV = filterString nV ","
  -- При выводе очередного узла его цвет определяем по коду НГ, ассоциируемой с узлом
  clr = case arrNV[4] of (
   " 219": [255, 0, 0]   -- Hard Drives
   " 226" : [0, 255, 0]  -- Mother Boards
   " 263" : [0, 0, 255]  -- CD
   " 297" : [255, 255, 0] -- Video Cards
   " 387" : [255, 0, 255] -- UPS
   " 218" : [0, 255, 255] -- Coolers
   defaut: [0, 0, 0]
  )
  plane width:w length:w pos:[xP, yP, 0] wireColor:clr lengthSegs:1 widthSegs:1
 )
)
close f

В полученном рисунке цвет узла СКК определяется по коду НГ, ассоциированной с выводимым узлом.

Карта Кохонена в задаче идентификации номенклатурных групп

Рис. 4. Представление обученной СКК

Заключение

НГ имеет подгруппы. Поэтому на рис. 4 имеются несвязанные области одного цвета: каждая область отвечает одной или нескольким подгруппам НГ верхнего уровня.
Задача идентификации СН при наличии обученной СКК решается следующим образом:

Приведенную схему реализует следующая функция:

функция найтиНГ(снТ, тзСКК)
 вв = создатьВВ(снТ);
 лу = найтиЛУ(тзСКК, вв);
 ув = тзСКК[лу[0]][лу[1]];
 кодНГ = ув[3];
 возврат кодНГ;
конецФункции

Литература

  1. Autodesk® 3ds Max® 2009 MAXScript Reference
  2. Каллан Р. Основные концепции нейронных сетей. - М. "Вильямс", 2001. 288 с.
  3. Люгер Д. Ф. Искусственный интеллект: стратегии и методы решения сложных проблем. - М. "Вильямс", 2005. 864 с.
  4. Синтаксис-помощник 1С:Предприятие.

Список работ

Рейтинг@Mail.ru