Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
DIPLOM1.DOC
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
4.81 Mб
Скачать

Глава 11. Создание программ для среды www

В настоящее время существуют две возможности для написания программ в Internet: программа может исполняться на сервере или же на клиентской машине (часто эти два способа комбинируются). Оба способа имеют свои достоинства и недостатки. Основным недостатком второго подхода является то, что результаты выполнения проверок невозможно регистрировать централизованно. В связи с этим программы обучения обязательно должны иметь серверную часть.

В случае необходимости можно добавить также программы, выполняемые на клиентской машине. Обычно эти программы разрабатываются с использованием языков Java, JavaScript или Visual Basic (ActiveX). Последняя технология поддерживается пока только продуктом Internet Explorer фирмы Microsoft. JavaScript в версии 1.2 на сегодня поддерживается программой Communicator фирмы Netscape (версия 1.1 поддерживается многими программами), а технология Java 1.1 хоть и претендует на стандарт, но фирмы-разработчики (в частности, Microsoft) стремятся расширить возможности языка, что может привести к несовместимости.

Из всего этого следует, что к написанию клиентских программ следует относиться с осторожностью – если программа работает в Вашей программе просмотра, не обязательно она будет работать и во всех остальных.

Серверные программы избавлены от несовместимости. Для нормального функционирования серверных программ часто необходима всего лишь поддержка клиентом стандарта HTML v.3, а эта возможность имеется в большинстве программ (включая и российские разработки, например, Ariadna).

Способ написания серверной программы зависит от платформы сервера. Например, на многих UNIX-платформах выбор ограничен программами CGI (Common Gateway Interface), которые пишутся обычно на языке C (или C++) или же на пакетных языках типа Perl. На многих рабочих станциях теперь функционирует также и язык Java.

В случае, когда используется сервер Netscape Fast Track возможна также поддержка NSAPI. Похожая технология применяется и в серверах Microsoft (IIS – Internet Information Server и персональный Web-сервер) – ISAPI. Смысл подобных расширений заключается в том, что код содержится в памяти в одном экземпляре (в виде динамически загружаемой библиотеки) в отличие от программ CGI, когда для каждого нового пользователя создается новая среда функционирования копии программы. Следует отметить, что программы CGI пишутся как консольные приложения, причем в режиме стандартного вывода они выводят содержимое гипертекстового файла.

В качестве примера программы, выполняемой на клиентской машине в проекте предлагается Java-апплет, иллюстрирующий работу самоорганизующихся карт Кохонена. Апплет позволяет изменять характеристики сети Кохонена и наблюдать за процессом автоматической классификации (кластеризации) образов. В данном случае сеть переводит 3-х мерное цветное RGB изображение на 2-х мерную сетку.

На рис. 1 изображено как выглядит окно броузера после входа на страницу с апплетом.

Рис. 1. Апплет, демонстрирующий работу самоорганизующихся карт Кохонена.

Здесь каждая ячейка сетки отображает один элемент (нейрон) нейронной сети. Круг отмечает нейроны-"победители". Цвет (состоящий из трех оттенков: красный, зеленый, синий) представляется в сети в виде числового вектора. При инициализации сети цвет ячеек выбирается случайным образом – эти значения и являются входным значением сети. Используя сигмоидальную функцию сеть Кохонена располагает похожие элементы рядом и подстраивает веса. Таким образом, апплет демострирует визуальное изображение топологической карты.

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

Нажатие на кнопку Start запускает процесс настройки карт. Остановить работу сети можно кнопкой Stop. Кнопка Reset предназначена для сброса всех характеристик сети и задания нового набора образов для работы сети (рис. 1).

Апплет позволяет менять различные характеристики сети и наблюдать происходящие изменения. Параметр Radius задает размер окрестности соседства и практически означает: сколько соседних элементов-клеток вокруг нейрона-"победителя" будут изменены (т. е. подстроены их веса). Изменение цвета ячеек сетки означает изменение значения "весов" элемента. Можно изменить окрестность соседства, выбрав определенный радиус в окне Radius, чем он больше тем больше будет подстраиваться. Также предоставляется возможность изменить скорость работы сети, при низкой скорости Slow можно пронаблюдать какие именно элементы изменяются, при высокой скорости Fast изменение сети и образование кластеров происходит более наглядно. Все эти настройки можно менять во время работы сети, не сбрасывая значения подстроенных весов. Для этого надо остановить работу сети, изменить ее характеристики и снова нажать кнопку Start.

Н а рис. 2 видно как сформировались кластеры при различных значениях радиуса окрестности.

Рис. 2. Сети Кохонена с сформированными кластерами.

Далее приведен текст апплета.

Листинг апплета sofm.java.

import java.awt.*;

import java.applet.*;

import java.util.*;

class synapse {

// this class models a weighted connection to a cell

double weight;

static double sharpness = 2.0; // higher values give sharper cutoff

cell c;

public static double sigmoid(double x) {

return 1.0 / (1.0 + Math.exp(-x));

}

public synapse(cell c, double dist) {

weight = 0.2 * sigmoid((sofm.limit - sharpness*dist)/sharpness);

// weight = 0.2 / (1.0 + dist); // use simple function

this.c = c;

}

public void update(double[] v, Graphics g) {

c.update(v, map.rate*weight, g);

}

}

class cell {

// has three basic properties: a grid position (gridv)

// an input space position (ipv)

// and a set of neighbours

double[] gridv;

double[] ipv;

int d;

static int neighboursEstimate = 30; // a rough estimate of number of neighbours - will grow automatically if needed to

Vector neighbours;

public cell(int d, int x, int y) {

// construct a cell with a d-dimensional randomly initialised vector at

// point x,y on the grid

ipv = new double[d];

gridv = new double[2];

gridv[0] = (double) x;

gridv[1] = (double) y;

this.d = d;

randCell();

neighbours = new Vector(neighboursEstimate);

}

public void randCell() {

for (int i=0; i<d; i++)

ipv[i] = Math.random();

}

public void addNeighbour(cell c) {

neighbours.addElement(new synapse(c, dist(gridv, c.gridv)));

}

public void removeNeighbours() {

neighbours.removeAllElements();

}

public void updateNeighbours(double[] v, Graphics g) {

// iterate over set of neighbours updating them

// in proportion to the connection weight

// now clearly identify the winner i.e. this cell

g.setColor(map.v2c(v));

g.fillOval((int)gridv[0]*map.size, (int)gridv[1]*map.size, map.size, map.size);

g.setColor(Color.black);

g.drawOval(1+(int)gridv[0]*map.size, 1+(int)gridv[1]*map.size, map.size-2, map.size-2);

try {Thread.sleep(100); }

catch (Exception e) {}

for (Enumeration e = neighbours.elements(); e.hasMoreElements(); )

((synapse) e.nextElement()).update(v, g);

}

public void update(double[] v, double w, Graphics g) {

// updates the vector of this cell using a weighted average

// of the current cell vector (ipv) and v

for (int i=0; i<d; i++) {

ipv[i] = (1.0 - w) * ipv[i] + w * v[i];

g.setColor(map.v2c(ipv));

g.fillRect((int)gridv[0]*map.size, (int)gridv[1]*map.size, map.size, map.size);

}

}

public static double sqr (double x) {

return x * x;

}

public static double dist(double[] x, double[] y) {

double dis = 0.0;

for (int i=0; i<x.length; i++)

dis += sqr(x[i] - y[i]);

return Math.sqrt(dis);

}

public double dist(double[] v) {

return dist(ipv, v);

}

}

class map {

// this class is to model the 2-d map with the underlying vectors

// can be set up to be a grid, each with a random neuron

// then each time, update it according to the chosen input vector

// this follows the cycle of pick winner, update neighbours

// (each cell includes itself in its set of neighbours)

int d; // the number of dimensions in input

int n; // the number of points on the grid

// cell[][] points;

static int size = 40; // number of pixels to use for each cell when drawing it

static double rate;

static double decayFac = 0.99; // no weight decay at present

Vector cells;

public map(int d, int n) {

this.n = n;

this.d = d;

cells = new Vector(n*n);

rate = 1.0;

makeMap();

setNeighbours();

}

public void makeMap() {

for (int i=0; i<n; i++)

for (int j=0; j<n; j++)

cells.addElement(new cell(d, i, j));

}

public void reset() {

rate = 1.0;

for (Enumeration e = cells.elements(); e.hasMoreElements();)

((cell) e.nextElement()).randCell();

}

public void setNeighbours() {

for (Enumeration e = cells.elements(); e.hasMoreElements();)

cellNeighbours((cell) e.nextElement());

}

public void cellNeighbours(cell c) {

c.removeNeighbours();

for (Enumeration e = cells.elements(); e.hasMoreElements();) {

cell cand = (cell) e.nextElement(); // cand is current candidate

if (cell.dist(c.gridv, cand.gridv) < sofm.limit)

c.addNeighbour(cand);

}

}

public cell getWinner(double[] v) {

double minDist = 100000.0; // choose this to be much bigger than a feasible distance

cell choice = null;

for (Enumeration e = cells.elements(); e.hasMoreElements();) {

cell cand = (cell) e.nextElement(); // cand is current candidate

double curDist = cand.dist(v);

if (curDist < minDist) {

choice = cand;

minDist = curDist;

}

}

return choice;

}

public void updateMap(Color c, Graphics g) {

update(c2v(c), g); // convert the colour to a vector

}

public void update(double[] v, Graphics g) {

cell winner = getWinner(v);

winner.updateNeighbours(v, g); // query this

rate *= decayFac;

}

public static double[] c2v(Color c) {

double[] v = new double[3];

v[0] = (double) c.getRed() / 255.0;

v[1] = (double) c.getGreen() / 255.0;

v[2] = (double) c.getBlue() / 255.0;

return v;

}

public static Color v2c(double[] v) {

return new Color((float) v[0], (float) v[1], (float) v[2]);

}

public void paint(Graphics g) {

for (Enumeration e = cells.elements(); e.hasMoreElements();) {

cell c = (cell) e.nextElement();

g.setColor(v2c(c.ipv));

g.fillRect((int)c.gridv[0]*size, (int)c.gridv[1]*size, size, size);

}

}

}

public class sofm extends Applet implements Runnable {

map sorg;

Thread animator;

int nCols = 8;

Color[] cols = new Color[8];

static double limit=5.0;

int sleepTime = 10;

Button start, stop, reset;

Choice neighbourhood, decay, speed; // decay not used

Panel buttons;

public void init() {

setControlPanel();

initColors();

sorg = new map(3,10);

animator = new Thread(this);

animator.start();

}

public void setControlPanel() {

setLayout(new BorderLayout());

buttons = new Panel();

start = new Button("start");

stop = new Button("stop");

reset = new Button("reset");

neighbourhood = new Choice();

neighbourhood.addItem("1.0");

neighbourhood.addItem("2.0");

neighbourhood.addItem("3.0");

neighbourhood.addItem("5.0");

neighbourhood.addItem("7.0");

neighbourhood.addItem("10.0");

neighbourhood.select("5.0");

speed = new Choice();

speed.addItem("fast");

speed.addItem("slow");

speed.select("fast");

buttons.add(new Label("Radius"));

buttons.add(neighbourhood);

buttons.add(start);

buttons.add(stop);

buttons.add(reset);

buttons.add(speed);

add("South", buttons);

}

public void initColors() {

cols[0] = Color.red;

cols[1] = Color.blue;

cols[2] = Color.green;

cols[3] = Color.white;

cols[4] = Color.black;

cols[5] = Color.magenta;

cols[6] = Color.pink;

cols[7] = Color.yellow;

}

public static int randInt(int range) {

return (int) (Math.random() * (double) range);

}

public void paint(Graphics g) {

validate();

sorg.paint(g);

}

public boolean handleEvent(Event event) {

// use 1.0 event model for maxmimum browser compatibilty

if (event.id == Event.ACTION_EVENT) {

if (event.target == start) {

// System.out.println("start");

animator.resume();

return true;

}

if (event.target == stop) {

animator.suspend();

return true;

}

if (event.target == reset) {

sorg.reset();

repaint();

return true;

}

if (event.target == speed) {

String s = speed.getSelectedItem();

if (s.equals("slow"))

sleepTime = 1000;

else

sleepTime = 10;

return true;

}

if (event.target == neighbourhood) {

limit = new Double(neighbourhood.getSelectedItem()).doubleValue();

// System.out.println(limit);

sorg.setNeighbours();

return true;

}

}

return super.handleEvent(event);

}

public void run() {

sorg.paint(getGraphics());

while(true) {

Color current = cols[randInt(nCols)];

Graphics g = getGraphics();

g.setColor(current);

g.fillRect(180, 410, 40, 30);

sorg.updateMap(current, g);

try { Thread.sleep(sleepTime); }

catch (Exception e) {

System.out.println(e);

}

}

}

}

В данном проекте для проведения контроля знаний применяется программа CGI созданная в среде Delphi, использующая обученную нейронную сеть для принятия решений.

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

Ознакомиться с работой программы можно по адресу http:\\158.250.47.76\index.html.

Далее приведен алгоритм работы программы.

Рис. 3. Схема алгоритма работы контролирующей программы.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]