книги / Эффективные методы решения задач кинематики и динамики робота-станка параллельной структуры
..pdf4.4. Применение НС для решения прямой задачи кинематики (ПЗК) 131
Далее проведем экспериментальную проверку решения ПЗК для трипода и гексапода на базе НС прямого распространения. Будем рассматривать влияние структуры и сложности НС, а также количе ства тренировочных примеров на точность решения и время обучения сети. В экспериментах использовалась программная реализация НС,
описание которой приведено |
выше, а полный код программы на языке |
C + + приведен в приложении |
1 . |
Данные для обучения и тестирования HC, включающие длины штанг и соответствующие им координаты рабочего инструмента, были получены посредствам решения обратной задачи кинематики, полу ченной в аналитическом виде (2.7-2.9 и 2.16-2.21). Таким образом, решения ПЗК каким-либо другим методом не требовалось. На вход обученной сети подавались тестовые значения длин штанг, на выходе получали декартовы координаты. В качестве варьируемых параметров выступали: количество точек тренировочного набора, изменяемое от 60 для 8000, число скрытых слоев, и число нейронов, составляющих скрытый слой. Ч исло нейронов принималось равным 10 и 60.
|
Количество нейронов |
|
|
|
--------- |
64 эл.в выборке |
— ----- |
4096 |
элементов |
— — |
729 элементов |
.----- |
8000 |
элементов |
Р ис. 4.6. Зависим ость ош ибки обучения от числа нейронов в скрытом слое
5 *
132 Гл. 4, Управление движением робота-станка на основе нейронных,,.
Как результат, был построен ряд зависимостей отображающих связь, числа нейронов, количества тренировочных точек с ошибкой по положению и времени обучения (рис. 4.5-4.8). Время затрачиваемое на обучение, сильно зависит от конфигурации используемого компью тера: частоты и разрядности процессора, объема оперативной памяти, поэтому на разных компьютерах будет наблюдаться разный результат.
На рис. 4.7 представлено семейство кривых, каждая из которых по лучена при количестве числа нейронов скрытого слоя для разного чис ла точек тренировочной выборки (60, 700, 1100, 8000). Из графиков видно, что при достаточном количестве примеров ошибка незначитель но уменьшается с увеличением числа нейронов в скрытом слое. Возрас тание ошибки на графиках соответствующих числу точек выборки 60 и 700 объясняется недостаточным их количеством. Таким образом, мож но сделать вывод о том, что количество точек тренировочного набора должно превышать количество связей в НС в не менее чем в пять раз.
Соотношения числа нейронов в скрытом слое и величины ошибки представлены на рис. 4.8. Данные зависимости указывают, что при
О1000 2000 3000 4000 5000 6000 7000 8000
Количество тренировочных точек
10 нейронов |
— ----- |
60 нейронов |
—— 10нейронов(2 слоя) — -----60 нейронов(2 слоя)
Рис. 4.7. Зависим ость времени обучения от объема выборки
4.4. Применение НС для решения прямой задачи кинематики (ПЗК) 133
--------- |
64 эл.в выборке |
............. |
4096 элементов |
--------- |
729 элементов |
--------- |
8000 элементов |
Рис. 4.8. Зависимость времени обучения от числа нейронов в скрытом слое
увеличении количества нейронов в сети, величина ошибки изменя ется мало, а время обучения сети, напротив значительно возрастает (рис. 4.8). Поэтому, выбирая структуру НС, следует ограничиться небольшим числом нейронов скрытого слоя.
Для увеличения точности обучения можно создавать цепочку уз лов из так называемых корректирующих НС. Данная сеть будет обу чаться на величинах ошибок, полученных при расхождении между результатами работы основной НС и истинными значениями. При этом корректирующая НС не привязана к конкретному диапазону измене ния длин штанг, а определяется лишь границами разброса ошибок, полученных в результате работы первой НС. Принцип работы такой цепочки НС сводится к следующему. На вход первой НС подаются значения длин штанг, на выходе получаем приближенное решение ПЗК. Далее полученные приближенные значения подаются на вход корректирующей сети и на выходе получаем более точное решение.
В таблице 4.1. приведены сравнительные характеристики рабо ты НС разной структуры. Как видно, применение корректирующих НС
134Гл. 4. Управление движением робота-станка на основе нейронных,,.
Та б л и ц а 4.1. Оценка точности решения ПЗК с помощью НС
Структура НС |
Характеристики |
Время |
Ошибка |
|
обучения НС, с |
обучения НС |
|||
|
|
|||
НС с одним |
20 нейронов, 8000 |
20,045 |
0,07984 |
|
скрытым слоем |
точек |
|||
|
|
|||
Многослойная НС |
3 слоя, 20 нейронов, |
124,325 |
0,04805 |
|
8000 точек |
||||
|
|
|
||
Корректирующая НС |
20 нейронов, 3 корр. |
102,034 |
0,00102 |
|
|
сети, 8000 точек |
|
|
является наиболее оптимальным вариантом, т. к. по сравнению с много слойной дает выигрыш и по величине ошибки, и по времени обучения. При этом следует заметить, что скорость прямого прохода по сети с корректирующей структурой будет также выше. Это объясняется тем, что каждая из сетей (основная и корректирующая) являются однослойными и по структуре являются более простыми, чем одна многослойная сеть.
Приложение 1 |
135 |
Приложение 1
la y e r.h
#ifndef ____ LAYER_H___
#define ____ LAYER_H___
#include <vector> #include <algorithm> #include <cmath> #include <fstream> #include <cstdlib> #include <iostream> #include "log.h"
typedef |
flo a t |
real; |
|
|
|
|
|
|
|
Pointer; |
|
|
||||||
typedef |
s td ::v e c to r < r e a l> ::ite ra to r |
|
|
|||||||||||||||
class |
Matrix: |
public |
std ::v ecto r< real> |
{ |
|
|
|
|||||||||||
p riv ate: |
|
|
row, |
col; |
|
|
|
|
|
|
|
|
|
|
|
|||
|
size _ t |
|
|
|
|
|
|
|
|
|
|
|
||||||
public: |
|
|
|
|
|
|
|
col(O) |
{} |
|
|
|
|
|
|
|||
|
M atrix():row (0), |
|
row(r), |
co l(c ), |
|
|||||||||||||
|
M atrix(size_t |
r, |
|
size _ t |
c): |
|
||||||||||||
|
Matrix(const |
Matrix |
&m): |
|
|
std::vector< real> (r*c) {} |
||||||||||||
|
std::vector<real>(m ) |
{ |
||||||||||||||||
|
|
|
row = m.getRow(); |
|
|
|
|
|
|
|
||||||||
|
} |
|
col |
= m.getCol(); |
|
|
|
|
|
|
|
|||||||
|
|
|
getRow() |
const |
{ |
return |
row; |
} |
|
|
||||||||
|
size _ t |
|
|
|||||||||||||||
|
size _ t |
getCol() |
const |
{ |
return |
col; |
} |
|
|
|||||||||
|
void |
se tS iz e (siz e _ t |
r, |
size _ t |
c) |
{ |
|
|
|
|||||||||
|
|
|
row |
= |
r; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
col |
= |
c; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
resiz e (r* c); |
|
|
|
|
|
|
|
|
|
||||||
|
|
& operator()(size_t |
i , size _ t |
j) |
{ |
return |
|
|||||||||||
|
re a l |
} |
||||||||||||||||
|
re a l |
o p e ra to r()(size _ t |
|
|
|
(* th is)[i* c o l+ j]; |
||||||||||||
|
i , size _ t |
j) |
const { return |
|||||||||||||||
|
void |
p rin t(std ::o stre a m |
&out) |
(* th is)[i* c o l+ j]; |
} |
|||||||||||||
}; |
const; |
|
|
|
||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class |
Layer |
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected: |
*pNext, |
*pPrev; |
|
|
|
|
|
|
|
|
||||||||
|
Layer |
|
|
|
|
|
|
|
|
|||||||||
|
in t row, |
col; |
sdvig, |
la stE rro r; |
|
|
|
|
||||||||||
|
Matrix |
weight, |
|
|
|
|
||||||||||||
|
Pointer |
input, |
erro r; |
|
|
errorBuf; |
|
|
||||||||||
|
std ::v ecto r< real> |
inputBuf, |
|
|
||||||||||||||
|
re a l |
BETTA, |
&NU; |
|
in t |
c); |
|
|
|
|
|
|
|
|||||
|
void |
i n i t ( i n t |
r, |
|
|
l./(l+exp(-x*BETTA)); } |
||||||||||||
|
re a l |
func(real |
x) |
{ |
return |
|||||||||||||
public: |
|
|
|
r, |
in t |
|
c, |
real |
&nu): |
row(r), |
c o l(c ), |
BETTA(l), |
||||||
|
Layer(int |
|
||||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
NU(nu), pNext(O), |
pPrev(O) { |
136 |
|
Приложение 1 |
|
} |
init(r, C ) ; |
|
|
|
|
|
|
void randorn{); |
|
|
|
void |
load{std::ifstrearn &in) |
|
|
void |
front(); |
= 0; |
|
virtual void back() |
std::vector<real |
||
void |
listOf(std::vector<real * > &W |
||
void |
zero(); |
* > & S , |
size_t pos); |
|
|
||
void |
setNext(Layer |
*n); |
|
void |
setPrev(Layer |
*p); |
|
Layer *next() { return pNext; } Layer *prev() { return pPrev; }
void setIn(Pointer p) { input = p; }
void setOut(Pointer p) { error = p; } Pointer getIn() { return inputBuf.begin(); }
Pointer getOut() { return errorBuf.begin(); }
void setBetta(real b) { BETTA = b; } real getBetta() { return BETTA; }
void print(int num = 1, std::ostream &out = std::cout);
};
class StartLayer: public Layer { public:
StartLayer(int r, int C , real &nu): Layer(r, C , void back();
};
class {\it MidlLayer}: public Layer { public:
MidlLayer(int r, int C , real &nu): Layer(r, C , void back();
};
nu) {}
nu) {}
struct Mem |
{ |
|
sizeOut; |
int |
sizeIn, |
||
Pointer |
in; |
|
|
Pointer out; |
int out): sizeln(in), sizeOut(out) {} |
||
Mem(int |
in, |
||
}; |
|
|
|
class Memory { private:
int sizeIn, sizeOut, count; std::vector<real> data;
public:
Memory():sizeIn(0), sizeOut(O), count(0) {} void setIn(int in) { sizeIn = in; }
void setOut(int out) { sizeOut = out; }
|
|
|
|
|
Приложение 1 |
|
|
|
|
137 |
||||
Mem |
operator[]{int |
i); |
} |
|
|
|
|
|
||||||
int |
size() |
{ return |
count; |
|
|
|
|
|
||||||
void read(char *name, int start, int end); |
|
|||||||||||||
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class {\it NeroNet} |
{ |
|
|
|
|
|
|
|
|
|
||||
private: |
|
|
|
|
|
arch; |
|
|
|
|
|
|
|
|
std::vector<int> |
|
sdvig; |
|
|
|
|||||||||
std::vector<real |
*> weight, |
|
|
|
||||||||||
std::vector<real> |
koef; |
|
|
|
|
|
|
|
||||||
Pointer error; |
|
|
|
|
|
|
|
|
|
|
||||
real |
sumError; |
|
|
|
|
|
|
|
|
|
|
|||
Layer |
*begin, |
*end; |
|
|
|
|
|
|
|
|
||||
Memory |
distr; |
|
|
|
|
|
|
|
|
|
|
|
||
real |
NU; |
|
|
|
|
|
|
|
|
|
|
|
|
|
void createLayers(); |
|
|
|
|
|
|
|
|||||||
void |
front(Mem mem); |
|
} |
|
|
|
|
|||||||
void |
zero() |
{ begin->zero(); |
|
|
|
|
||||||||
public: |
|
|
|
*name); |
|
|
|
|
|
|
|
|||
NeroNet(char |
|
|
|
|
|
end(O) |
{ |
|||||||
NeroNet(std::vector<int> &v): begin(O), |
||||||||||||||
|
|
arch |
= v; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
createLayers(); |
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
\sim NeroNet(); |
|
|
|
|
|
} |
|
|
|
|
||||
void print() |
{ begin->print(); |
|
|
to) |
|
|||||||||
void loadData(char |
*name, |
int |
from, int |
to);} |
||||||||||
real |
frontBack(); |
|
{distr.read(name, from, |
|||||||||||
|
|
|
|
|
|
|
|
|
||||||
real |
front(); |
|
|
|
|
|
|
|
|
|
|
|
||
void |
saveOld(); |
|
|
|
|
|
|
|
|
|
|
|||
void |
loadOld(); |
|
|
|
} |
|
|
|
|
|
|
|||
real |
getNu() |
{ return NU; |
|
|
|
|
|
|
||||||
real |
&setNu() |
{ return NU; |
} |
|
|
} |
|
|
||||||
real |
getError() |
{ return sumError; |
|
|
||||||||||
std::vector<real |
*> |
&getWeight() |
{ return weight; } |
|||||||||||
std::vector<real |
*> |
&getSdvig() |
{ return |
sdvig; |
} |
|||||||||
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
externstd::ostream |
&operator<<(std::ostream& |
Mem &mem); |
||||||||||||
|
|
|
|
|
|
|
|
|
|
out, |
const |
extern std::ostream &operator<<(std::ostream &out, Matrix &m); extern std::ostream &operator<<(std::ostream &out,
std::vector<real> &m);
#endif
layer.epp
#include layer.h using namespace std;
void Matrix::print(ostream &out) const {
138 |
Приложение 1 |
for{size_t i=0; i<row; ++i) { for{size_t j=0; j<col; ++j)
out<<(*this)[i*col+j]<<'\backslash t'; if (i != row-l)out<<endl;
}
}
void Layer::init(int r, int c) { r++;
weight.setSize(r, c); weight.assign(r*c, 0); sdvig.setSize(r, c); sdvig.assign(r*c, 0); lastError.resize(r*c); inputBuf.resize(c); errorBuf.resize(c);
}
void Layer::front() {
for(int i=0; i<weight.getCol(); ++i) { real tmp = weight(0, i);
Pointer it = input;
for(int j=l; j < weight.getRow(); ++it, ++j) tmp +=(*it)*weight(j, i);
inputBuf[i] = func(tmp);
}
DEBUG(endl<<input: <<endl<<inputBuf); Layer *point = next();
if (point) point->front();
}
void Layer::listOf(vector<real *> &w, vector<real * > & S , size_t pos) {
for(size_t i=0; i<weight.getRow(); ++i) for(size_t j=0; j<weight.getCol(); ++j) {
w[pos] = &weight(i, j); s[pos++] = &sdvig(i, j);
}
Layer *point = next();
if (point) point->listOf(w, s, pos);
}
void Layer::zero() { sdvig.assign((row+l)*col, 0); Layer *point = next();
if (point) point->zero();
}
void Layer::setNext(Layer *n) { pNext = n;
}
void Layer::setPrev(Layer *p) { pPrev = p;
|
|
|
Приложение 1 |
139 |
||
} |
if (р) |
(input = p->getln{); error = p->getOut{);} |
||||
|
|
|
|
|
|
|
void Layer::print(int |
num, std::ostream &out) ( |
|||||
|
out«Sloy <<num<<std::endl; |
|
||||
|
weight.print(out); |
|
|
|||
|
out<<std::endl; |
|
|
|
|
|
|
Layer *point = next(); |
|
|
|||
} |
if (point) point->print(num+l, out); |
|
||||
|
|
|
|
|
|
|
void |
StartLayer::baok() |
( |
|
( |
||
|
for(size_t i = 0; |
i<errorBuf.size(); ++i) |
||||
|
errorBuf[i] = inputBuf[i]*(l-inputBuf[i])*errorBuf[i]; |
|||||
|
real adjust |
= NU*errorBuf[i]; |
|
|||
|
sdvig(0,i) |
= sdvig(0,i) + adjust; |
|
|||
|
} |
itln = |
input; |
|
|
|
|
Pointer |
|
|
|||
|
for(size_t i=l; i<weight.getRow(); ++i, ++itln) ( |
|||||
|
for(size_t j=0; j<weight.getCol(); ++j) ( |
|||||
|
|
real |
adjust |
= NU*errorBuf[j]*(*itIn); |
||
|
} |
sdvig(i,j) |
= sdvig(i,j) + adjust; |
|||
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
DEBUG(endl<<sigma: <<endl<<errorBuf); |
|
||||
} |
DEBUG(endl<<sdvid: <<endl<<sdvig); |
|
||||
|
|
|
|
|
|
|
void |
(\it MidlLayer}::baok() |
( |
( |
|||
|
for(size_t i = 0; |
i<errorBuf.size(); ++i) |
||||
|
errorBuf[i] = inputBuf[i]*(l-inputBuf[i])*errorBuf[i]; |
|||||
|
real adjust |
= NU*errorBuf[i]; |
|
|||
|
sdvig(0,i) |
= sdvig(0,i) + adjust; |
|
|||
|
} |
itErr = error; |
|
|
||
|
Pointer |
|
|
|||
|
Pointer |
itIn = |
input; |
|
|
|
|
for(int i=l; i<weight.getRow(); ++i, ++itErr, ++itIn) ( |
|||||
|
real tmp |
= |
0; |
|
|
|
|
for(int j=0; j<weight.getCol(); ++j) ( |
|||||
|
|
real |
adjust |
= NU*errorBuf[j]*(*itIn); |
||
|
|
sdvig(i,j) = sdvig(i,j) + adjust; |
||||
|
} |
tmp |
+= errorBuf[j]*weight(i, |
j); |
||
|
|
= tmp; |
|
|
||
|
(*itErr) |
|
|
|||
|
} |
|
|
|
|
|
Layer *point = prev(); point->baok();
DEBUG(endl<<sigma: <<endl<<errorBuf); DEBUG(endl<<sdvid: <<endl<<sdvig);
}
Mem Memory::operator[](int i) (
if (i>=count $\vert \vert $ i<0) ERROR(Out of range);
140 |
Приложение 1 |
Mem mem(sizeIn, |
sizeOut); |
mem.in = data.begin()+i*(sizeIn+sizeOut);
mem.out = data.begin() + i*(sizeIn+sizeOut) + sizeIn; return mem;
}
void Memory::read(ohar *name, int start, int end) { int C ;
data.clear(); count = 0;
ifstream file(name); file>>c;
if (c<end) ERROR(Out of range in file «name); real tmp;
for(int k=0; k<start; ++k) {
for(int i=0; i<sizeIn; ++i) file>>tmp; for(int i=0; i<sizeOut; ++i) file>>tmp;
}
for(int k=start; k<end; ++k) { for(int i=0; i<sizeIn; ++i) {
file>>tmp; data.push_back(tmp);
}
for(int i=0; i<sizeOut; ++i) { file>>tmp; data.push_back(tmp);
}
count++;
}
}
void {\it NeroNet}::createLayers() { Layer *prev, *curr;
size_t ratioCount = 0;
size_t count = arch.size() - 1;
if (Icount) ERROR(Incorrect arch nero net);
end = begin = prev = new StartLayer(arch[0], arch[1], NU); ratioCount = (arch[0]+1)*arch[1];
if (count == 1) return; for(size_t i=1; i<count-1; ++i) {
ratioCount += (arch[i]+1)*arch[i+1];
curr = new {\it MidlLayer}(arch[i], arch[i+1], NU); curr->setPrev(prev);
prev->setNext(curr); prev = curr;
}
ratioCount += (arch[count-1] + 1)*arch[count];
end = new {\it MidlLayer}(arch[count-1], arch[count], NU); end->setPrev(prev);
prev->setNext(end); distr.setIn(arch[0]); distr.setOut(arch[count]); error = end->getOut(); weight.resize(ratioCount); sdvig.resize(ratioCount);