Khi mạng nơron huấn luyện, nó được đưa vào với các tập huấn luyện. Sau đó, kết quả thu được từ mạng nơron sẽ được so sánh với kết quả trước đó. Phần được thêm vào để làm cho dữ liệu đầu ra hiện tại phù hợp với dữ liệu đầu ra trước đó được gọi là sai số.
Có vài cách để giảm hàm sai số này về mức tối thiểu . Phổ biến nhất là sử dụng phương thức giảm theo gradient. Thuật toán để ước lượng đạo hàm của hàm sai số được biết đến như là thuật toán lan truyền ngược, đó là nó lan truyền các sai số này ngược lại trong mạng.
Trong thực tế, phương thức lan truyền ngược hoạt động bằng cách; đầu tiên nó chạy một chương trình nhận dạng đối với dữ liệu huấn luyện để thu được một ma trận trọng số, và sau đó điều chỉnh các trọng số và độ lệch để cải thiện sai số.
Ở trên ta thấy rằng phương thức Layer.run được chỉ ra trong danh dách 5.1, chịu trách nhiệm cung cấp dữ liệu huấn luyện cho mạng nơron. Nó sẽ được chạy đối với từng tập huấn luyện, và dữ liệu huấn luyện sẽ được chạy lặp đi lặp lại cho đến khi sai số của mạng nơron nằm trong mức cho phép.
Phương thức SynapsẹrevPut sẽ gọi phương thức Synapsẹbackward để điều chỉnh các độ lệch cần thiết của nơron bất kỳ. Hoạt động của phương thức Synapsẹbackward được chỉ ra trong danh sách 2.6.
Danh sách 2.6: Phương thức Synapsẹbackward protected void backward(double[] pattern) { int x;
int y;
double s, dw;
int m_rows = getInputDimension(); int m_cols = getOutputDimension(); // Điều chỉnh các trọng số
for ( x=0; x < m_rows; ++x ) { double absv;
s = 0;
for ( y=0; y < m_cols; ++y ) {
if ( getMomentum() < 0 ) { if ( pattern[y] < 0 ) absv = -pattern[y]; else
absv = pattern[y];
dw = getLearningRate() * pattern[y] * inps[x] + absv * arraỵdelta[x][y];
} else
dw = getLearningRate() * pattern[y] * inps[x] + getMomentum() * arraỵdelta[x][y]; arraỵvalue[x][y] += dw; arraỵdelta[x][y] = dw; } bouts[x] = s; } } 2.3.3 Các tập huấn luyện
Để huấn luyện mạng nơron Kohonen thì các tập huấn luyện phải được cung cấp. Dữ liệu huấn luyện này sẽ được lưu trữ trong lớp TrainingSet, lớp này được thiết kế để nó là một lớp chứa đựng dữ liệụ Lớp TrainingSet quản lý hai biến mảng độ dài của đầu vào và đầu rạ
Trong lớp TrainingSet, nó lưu trữ các biến và cách sử dụng chúng được tóm tắt như sau:
• inputCount - Số lượng các phần tử đầu vào sẽ có cho từng mẫu huấn luyện. • outputCount - Số lượng các phần tử đầu ra sẽ có cho từng mẫu huấn luyện. • input[][] - Các mẫu đầu vào huấn luyện.
• output[][] - Các mẫu đầu ra huấn luyện. • trainingSetCount - Số lượng mẫu huấn luyện.
Do quá trình hoạt động của mạng nơron Kohonen là không có giám sát, chỉ có các phần tử dữ liệu đầu vào được cung cấp, nên đối tượng TrainingSet được xây dựng để huấn luyện cho ra dữ liệu đầu ra, nó sẽ được chuyển tới đối tượng KohonenNetwork để huấn luyện trong lần tiếp theọ
2.3.4 Báo cáo tiến trình
Để chuẩn bị các tập huấn luyện và thu nhận tình trạng thông tin từ quá trình huấn luyện, ta phải hiểu rõ các lớp mạng Kohonen hoạt động như thế nàọ Chúng ta sẽ bắt đầu bằng việc xem xét lớp mạng cơ sở.
2.3.4.1 Lớp mạng cơ sở
Bây giờ chúng ta sẽ xem xét lớp Network. Lớp này là lớp cơ sở cho lớp KohonenNetwork, nó là lớp cuối cùng cung cấp cho mạng nơron Kohonen.
Việc tính toán chiều dài một vector là một phần quan trọng của mạng nơron Kohonen. Lớp Network chứa một phương thức để tính toán độ dài vector của vector đã cho, và nó được biểu diễn dưới dạng mảng. Phương thức này được chỉ ra trong danh sách 2.7.
Danh sách 2.7: Tính độ dài của một vector (Network.java) /**
* @Tham số v vector
* @Kết quả trả về độ dài vector. */
static double vectorLength( double v[] ) {
double rtn = 0.0 ;
for ( int i=0;i<v.length;i++ ) rtn += v[i] * v[i];
return rtn; }
Một chức năng quan trong khác được cung cấp bởi lớp cơ sở Network là chức năng tính toán tích vô hướng. Lớp Network của mạng Kohonen dùng phương thức này để tính toán dữ liệu đầu ra của mạng nơron. Phương thức tính tích vô hướng được chỉ ra trong danh sách 2.8.
Danh sách 2.8: Tính toán tích vô hướng (Network.java) /**
* @Tham số vec1 là vector thứ nhất * @Tham số vec2 là vector còn lại * @Kết quả trả về là tích vô hướng. */
double dotProduct(double vec1[] , double vec2[] ) { int k,v; double rtn; rtn = 0.0; k = vec1.length; v = 0; while ( (k--)>0 ) { rtn += vec1[v] * vec2[v]; v++; } return rtn; }
Đầu tiên, các trọng số giữa nơron được khởi tạo với các giá trị ngẫu nhiên. Sau đó các giá trị ngẫu nhiên này được huấn luyện để cho ra các kết quả tốt hơn. Tại thời điểm bắt đầu của mỗi chu kỳ huấn luyện, các trọng số cũng được khởi tạo với các giá trị ngẫu nhiên. Lớp Network cung cấp một phương thức để thực thi vấn đề nàỵ Danh sách 2.9 chỉ ra phương thức để sinh các trọng số ngẫu nhiên.
Danh sách 2.9: Khởi tạo các trọng số ngẫu nhiên (Network.java) /**
* @Tham số weight là một ma trận trọng số. */
void randomizeWeights( double weight[][] ) {
double r ;
int temp = (int)(3.464101615 / (2. * Math.random() )); for ( int y=0;y<weight.length;y++ ) {
for ( int x=0;x<weight[0].length;x++ ) {
r = (double) random.nextInt(Integer.MAX_VALUE) + (double) random.nextInt(Integer.MAX_VALUE) -
(double) random.nextInt(Integer.MAX_VALUE) - (double) random.nextInt(Integer.MAX_VALUE) ; weight[y][x] = temp * r ; } } } }
2.3.4.2 Lớp KohonenNetwork
Chúng ta sẽ xem xét lớp KohonenNetwork. Lớp này là lớp thực thi mạng nơron Kohonen. Lớp KohonenNetwork có một số tính chất được chỉ ra trong danh sách 2.10.
Danh sách 2.10: Các tính chất của lớp KohonenNetwork public class KohonenNetwork extends Network {
double outputWeights[][]; protected int learnMethod = 1; protected double learnRate = 0.5; protected double quitError = 0.1; protected int retries = 10000; protected double reduction = .99; protected NeuralReportable owner; public boolean halt = false; protected TrainingSet train;
Các tính chất được mô tả như sau:
• halt – Thiết lập này là xác thực hủy bỏ quá trình huấn luyện.
• learnMethod – Tỷ lệ học, đặt bằng 1.
• learnRate – Tỷ lệ học ban đầụ
• outputWeights[][] – Các trọng số của các nơron đầu ra dựa trên đầu vàọ
• owner – Lớp owner, lớp này thực thi giao diện NeuralReportablẹ
• quitError – Khi tỷ lệ sai số đạt đến mức nhỏ hơn 10% thì dừng huấn luyện.
• reduction – Lượng giảm tỷ lệ học ban đầu (learnRate) bởi mỗi công đoạn.
• retries - Tổng số chu kỳ cho phép, nó đặt một mức trần (a ceiling) số lượng các chu kỳ huấn luyện có thể xảy rạ
• train – Tập huấn luyện.
Để cho mạng nơron Kohonen hoạt động tốt, ta không chỉ chuẩn hóa vector đầu vào, mà ta còn phải chuẩn hóa cả ma trận trọng số. Danh sách 2.13 chỉ ra một phương thức để chuẩn hóa các dữ liệu đầu vào để đưa tới mạng nơron Kohonen, và danh sách 2.14 chỉ ra sự chuẩn hóa ma trận trọng số.
Danh sách 2.13: Chuẩn hó dữ liệu đầu vào (KohonenNetwork.java) /**
* @Tham số input là mẫu dữ liệu vào * @Tham số normfac là nhân tố chuẩn hóa
* @Tham số synth là giá trị đầu vào cuối cùng */
void normalizeInput(
final double input[] , double normfac[] ,
double synth[] )
{
double length, d ;
length = vectorLength ( input ) ;
// Điều chỉnh trong trường hợp độ dài quá nhỏ if ( length < 1.E-30 )
length = 1.E-30 ;
normfac[0] = 1.0 / Math.sqrt ( length ) ; synth[0] = 0.0 ;
}
Danh sách 2.14: Chuẩn hóa trọng số (KohonenNetwork.java) /**
* @Tham số w là các trọng số đầu vào */
void normalizeWeight( double w[] ) {
int i ; double len ;
len = vectorLength ( w ) ;
// Điều chỉnh trong trường hợp độ dài quá nhỏ if ( len < 1.E-30 )
len = 1.E-30 ;
len = 1.0 / Math.sqrt ( len ) ;
for ( i=0 ; i<inputNeuronCount ; i++ ) w[i] *= len ;
w[inputNeuronCount] = 0; }
Bây giờ ta kiểm tra phương thức thử mẫu, được sử dụng để đưa mẫu dữ liệu đầu vào tới mạng nơron Kohonen. Phương pháp này được gọi là phương pháp thử “trial”, được chỉ ra trong danh sách 2.15.
Danh sách 2.15: Thử mẫu vào (KohonenNetwork.java) /**
* Phương thức này có thể được sử dụng khi đưa một mẫu tới mạng. * Thường thường, nó hay dùng để gọi nơron thắng
* @Tham số input là mẫu vàọ */
void trial ( double input[] ) {
int i ;
double normfac[]=new double[1], synth[]=new double[1], optr[]; normalizeInput(input,normfac,synth) ;
for ( i=0 ; i<outputNeuronCount; i++ ) { optr = outputWeights[i];
output[i] = dotProduct( input , optr ) * normfac[0] + synth[0] * optr[inputNeuronCount] ; // Tạo bản đồ lưỡng cực mới (từ -1,1 tới 0,1) output[i] = 0.5 * (output[i] + 1.0) ; // Tính toán làm tròn if ( output[i] > 1.0 ) output[i] = 1.0 ; if ( output[i] < 0.0 ) output[i] = 0.0 ; } }
Vậy quá trình tính toán giá trị cho mỗi nơron đầu ra được tính toán bằng cách lấy tích vô hướng đã được chuẩn hóa của dữ liệu đầu vào và các trọng số. Do dữ liệu đầu ra cuối cùng có thể lớn hơn 1 hoặc nhỏ hơn 0, ta phải đưa nó về khoảng [0,1]. Để đưa dữ liệu đầu ra về khoảng [0,1] thì các kết quả nhỏ hơn 0 thì ta đưa nó về 0, và các kết quả lớn hơn 1 được đưa về 1. Dữ liệu đầu ra cuối cùng của mỗi nơron được lưu trữ trong mảng dữ liệu đầu rạ
Chúng ta chỉ quan tâm đến nơron thắng vì chúng được đưa lại vào mẫu để huấn luyện. Danh sách 2.16 chỉ ra phương thức để đưa một mẫu dữ liệu đầu vào tới
mạng Kohonen, và thu nhận nơron thắng. Phương thức này chính là phương thức dùng để phận loại mẫu trong mạng Kohonen.
Danh sách 2.16: Đưa ra một mẫu vào và thu nhận nơron thắng /**
* @Tham số input là mẫu vào
* @Tham số normfac là nhân tố chuẩn hóa
* @Tham số synth là giả đầu vào cuối cùng – (synthetic last input)
* @Kết quả trả về là số nơron thắng. */
public int winner(double input[] ,double normfac[] ,double synth[])
{
int i, win=0;
double biggest, optr[];
normalizeInput( input , normfac , synth ) ; // Chuẩn hóa dữ liệu đầu vào
biggest = -1.E30;
for ( i=0 ; i<outputNeuronCount; i++ ) { optr = outputWeights[i];
output[i] = dotProduct (input , optr ) * normfac[0] + synth[0] * optr[inputNeuronCount] ; // Tạo bản đồ lưỡng cực mới (từ -1,1 tới 0,1) output[i] = 0.5 * (output[i] + 1.0) ;
if ( output[i] > biggest ) { biggest = output[i] ; win = i ;
}
// account for rounding if ( output[i] > 1.0 ) output[i] = 1.0 ; if ( output[i] < 0.0 ) output[i] = 0.0 ; } return win ; }
Phương thức này sẽ thường xuyên được sử dụng khi ta muốn đưa một mẫu tới một mạng nơron để phân loạị Còn phương thức “thử” mà chúng ta vừa xem xét ở
trên chỉ được sử dụng trong khi huấn luyện mạng. Khi huấn luyện, chúng ta quan tâm đến dữ liệu đầu ra hiện tại của mỗi nơron. Trái lại, khi phân loại mẫu thì chúng ta chỉ quan tâm đến nơron thắng.
Phương thức winner lặp qua mỗi nơron đầu ra và tính toán dữ liệu đầu ra cho mỗi nơron riêng biệt. Trong quá trình lặp này thì các chỉ số của nơron được lưu lạị Chỉ số này là của nơron có giá trị đầu ra cao nhất. Nơron có chỉ số cao nhất được gọi là nơron thắng. Nơron thắng này được trả về tập mẫu để tiếp tục tham gia vào quá trình huấn luyện.
Bây giờ chúng ta bắt đầu xem xét quá trình huấn luyện. Phương thức training
được chỉ ra trong danh sách 2.17.
Danh sách 2.17: Huấn luyện mạng nơron (KohonenNetwork.java) /**
* @exception javạlang.RuntimeException */
public void learn () throws RuntimeException {
int i, key, tset,iter,n_retry,nwts; int won[],winners ;
double work[],correc[][],rate,best_err,dptr[]; double bigerr[] = new double[1] ;
double bigcorr[] = new double[1];
KohonenNetwork bestnet; // Preserve best here totalError = 1.0 ;
bestnet = new
KohonenNetwork(inputNeuronCount,outputNeuronCount,owner) ; won = new int[outputNeuronCount];
correc = new double[outputNeuronCount][inputNeuronCount+1]; if ( learnMethod==0 )
work = new double[inputNeuronCount+1]; else work = null ; rate = learnRate; initialize () ; best_err = 1.e30 ; // Vòng lặp chính: n_retry = 0 ;
for ( iter=0 ; ; iter++ ) {
evaluateErrors ( rate , learnMethod , won , bigerr , correc , work ) ;
totalError = bigerr[0] ;
if ( totalError < best_err ) { best_err = totalError ;
copyWeights ( bestnet , this ) ; }
winners = 0 ;
for ( i=0;i<won.length;i++ ) if ( won[i]!=0 )
winners++;
if ( bigerr[0] < quitError ) break ;
if ( (winners < outputNeuronCount) && (winners < train.getTrainingSetCount()) ) { forceWin ( won ) ;
continue ; }
adjustWeights ( rate , learnMethod , won , bigcorr, correc ) ; owner.update(n_retry,totalError,best_err); if ( halt ) { owner.update(n_retry,totalError,best_err); break; } Thread.yield(); if ( bigcorr[0] < 1E-5 ) { if ( ++n_retry > retries ) break ; initialize () ; iter = -1 ; rate = learnRate ; continue ; } if ( rate > 0.01 ) rate *= reduction ; } // Hoàn thành
copyWeights( this , bestnet ) ;
for ( i=0 ; i<outputNeuronCount ; i++ ) normalizeWeight ( outputWeights[i] ) ; halt = true;
n_retry++;
owner.update(n_retry,totalError,best_err); }
Phương thức training bắt đầu bằng việc khởi tạo các ma trận trọng số với các giá trị ngẫu nhiên và điều chỉnh các giá trị trọng yếu khác. Khi khởi tạo xong, vòng lặp chính đưa các mẫu huấn luyện tới mạng nơron và tính toán các sai số dựa trên các kết quả thu được từ mạng nơron. Khi kết thúc vòng lặp chính, xác định được nơron thắng, và nó sẽ tiếp tục huấn luyện để thúc đẩy hợp nhất các khả năng của nó trong quá trình nhận dạng mẫu riêng biệt, đồng thời cho ra một ma trận trọng số tốt hơn. Điều này được xác định bằng cách tính toán sự cải tiến sai số giữa công đoạn hiện tại và công đoạn trước. Nếu sự cải tiến không đáng kể thì chu kỳ huấn luyện này coi như hoàn thành, và lại bắt đầu một chu kỳ mớị
Vì chu kỳ huấn luyện cực nhanh nên chúng ta chỉ theo dõi chu kỳ có tỷ lệ sai số tốt nhất. Khi chúng ta tìm thấy một ma trận trọng số có sai số nằm dưới mức sai số
cho phép, thì sự huấn luyện được hoàn thành. Ngược lại, thì chúng ta sẽ lấy ma trận tốt nhất đã được xác định ở chu kỳ trước.
Bây giờ, chúng ta sẽ bắt đầu xem xét xem các sai số được ước lượng như thế nàọ Danh sách 2.18 chỉ ra ước lượng các sai số.
Danh sách 2.18: Ước lượng các sai số (KohonenNetwork.java) /**
* Phương thức này dùng trong quá trình học. Nó dùng để ước lượng các trọng số dựa vào tập huấn luyện.
* @Tham số rate là tỉ lệ học
* @Tham số learn_method là dùng phương thức method(0=ađitive, 1=subtractive)
* @Tham số won là quản lý số lần nơron thắng * @Tham số bigerr là trả về sai số
* @Tham số correc là trả về mảng hiệu chỉnh * @Tham số work là phạm vi hoạt động
* @exception javạlang.RuntimeException */ */ void evaluateErrors ( double rate , int learn_method , int won[], double bigerr[] , double correc[][] , double work[]) throws RuntimeException {
int best, size,tset ;
double dptr[], normfac[] = new double[1];
double synth[]=new double[1], cptr[], wptr[], length, diff ; // Hiệu chỉnh và đặt lại số lần thắng
for ( int y=0;y<correc.length;y++ ) { for ( int x=0;x<correc[0].length;x++ ) { correc[y][x]=0;
} }
for ( int i=0;i<won.length;i++ ) won[i]=0;
bigerr[0] = 0.0 ;
// Lặp qua tất cả các tập huấn luyện để xác định giá trị hiệu chỉnh for ( tset=0 ; tset<train.getTrainingSetCount(); tset++ ) { dptr = train.getInputSet(tset);
best = winner ( dptr , normfac , synth ) ; won[best]++;
wptr = outputWeights[best]; cptr = correc[best];
length = 0.0 ;
for ( int i=0 ; i<inputNeuronCo the unt ; i++ ) { diff = dptr[i] * normfac[0] - wptr[i] ;
length += diff * diff ; if ( learn_method!=0 ) cptr[i] += diff ;
else
work[i] = rate * dptr[i] * normfac[0] + wptr[i] ; }
diff = synth[0] - wptr[inputNeuronCount] ; length += diff * diff ;
if ( learn_method!=0 )
cptr[inputNeuronCount] += diff ; else
work[inputNeuronCount] = rate * synth[0] + wptr[inputNeuronCount] ;
if ( length > bigerr[0] ) bigerr[0] = length ; if ( learn_method==0 ) { normalizeWeight( work ) ;
for ( int i=0 ; i<=inputNeuronCount ; i++ ) cptr[i] += work[i] - wptr[i] ;
} }
bigerr[0] = Math.sqrt ( bigerr[0] ) ; }
Mạng được huấn luyện và tạo ra một mảng hiệu chỉnh chứa biến hiệu chỉnh được tạo bởi phương thức adjustWeights.
Sau khi nhân tố hiệu chỉnh được tính toán, thì các trọng số phải được điều chỉnh. Danh sách 2.19 chỉ ra việc điều chỉnh các trọng số.
Danh sách 2.19: Hiệu chỉnh các trọng số (KohonenNetwork.java) /**
* Phương thức này được gọi vào cuối mỗi lần huấn luyện, và nó điều chỉnh các trọng số dựa vào lần thử trước.
* @Tham số rate là tỉ lệ học
* @Tham số learn_method sử dụng phương pháp method(0=ađitive, 1=subtractive)
* @Tham số won quản lý số lần mỗi nơron thắng * @Tham số bigcorr dùng để quản lý sai số
* @Tham số correc dùng để quản lý mảng hiệu chỉnh */ void adjustWeights ( double rate , int learn_method , int won[] , double bigcorr[], double correc[][] ) {
double corr, cptr[], wptr[], length, f ; bigcorr[0] = 0.0 ;
for ( int i=0 ; i<outputNeuronCount ; i++ ) { if ( won[i]==0 ) continue ; wptr = outputWeights[i]; cptr = correc[i]; f = 1.0 / (double) won[i] ; if ( learn_method!=0 ) f *= rate ;
length = 0.0 ;
for ( int j=0 ; j<=inputNeuronCount ; j++ ) { corr = f * cptr[j] ;
wptr[j] += corr ; length += corr * corr ; }
if ( length > bigcorr[0] ) bigcorr[0] = length ; }
// Tính toán nhân tố hiệu chỉnh
bigcorr[0] = Math.sqrt ( bigcorr[0] ) / rate ; }
2.4 Kết luận
Trong chương này, chúng ta đã tìm hiểu về mạng nơron Kohonen. Mạng nơron Kohonen khác với mạng lan truyền ngược ở vài điểm. Mạng nơron Kohonen là luyện không giám sát. Điều này có nghĩa rằng, mạng nơron Kohonen được cho dữ liệu đầu vào nhưng không biết trước được “cái ra”. Sau đó, trong khi huấn luyện thì