Thực thi mạng nơron Kohonen

Một phần của tài liệu Nhận dạng ký tự quang sử dụng mạng nơron kohonen (Trang 52)

Có vài lớp có thể được sử dụng cùng nhau để tạo ra một mạng nơron Kohonen. Chương sau ta sẽ chỉ ra cách để xây dựng một ứng dụng dựa trên mạng nơron Kohonen, đó là ứng dụng nhận dạng ký tự quang (cụ thể là nhận dạng chữ viết tay). Các lớp được mô tả như sau:

· KohonenNetwork – Thực thi các phương thức thuộc về mạng nơron Kohonen. Đây là nơi mà mạng nơron Kohonen được huấn luyện và lấy các mẫụ

· Network – Chứa đựng các phương pháp không thuộc về mạng nơron Kohonen. Các lớp này chứa các phương pháp để tính toán tích vô hướng, và chiều dài vector.

· NeuralReportable – Một giao diện đơn giản cho phép mạng nơron Kohonen trả về thông tin tiến bộ sau khi mạng nơron được huấn luyện. · TrainingSet – Một tập huấn luyện chứa đối tượng, đó là có thể chứa các mảng của các lần huấn luyện riêng lẻ. Tập huấn luyện có thể chứa cả các phần tử dữ liệu đầu vào và dữ liệu đầu rạ

Các lớp này hoạt động cùng nhau để cung cấp các chức năng cho mạng Kohonen. Trước tiên, ta xem việc thực thi mạng nơron truyền thẳng xem chúng hoạt động như thế nàọ

2.3.1 Thực thi mạng nơron truyền thẳng

Khi thực thi mạng nơron truyền thẳng, dữ liệu được truyền đến đối tượng “synapse”. Phương thức run chạy một vòng lặp luôn luôn đợi mẫu dữ liệu mới và sau đó chuyển chúng thành dữ liệu đầu ra, danh sách 2.1 chỉ ra phương thức run hoạt động để nhớ mẫu của lớp Layer.

Danh sách 2.1: Phương thức Layer.run public void run() {

while ( running ) { int dimI = getRows();

int dimO = getDimension(); // Nhớ mẫu

this.fireFwdGet(); if ( m_pattern != null ) { forward(inps); m_pattern.setArray(outs); fireFwdPut(m_pattern); } if ( step != -1 )

// Kiểm tra nếu bước tiếp theo đang được học m_learning = monitor.isLearningCicle(step); else

// Dừng mạng running = false;

//Nếu ((m_learning) && (m_batch != 1)) if ( (m_learning) && (running) )

{ // Học

gradientInps = new double[dimO]; fireRevGet();

backward(gradientInps);

m_pattern = new Pattern(gradientOuts); m_pattern.setCount(step);

fireRevPut(m_pattern); }

} // Kết thúc while (running = false) myThread = null;

}

Phương thức fireFwdPut được gọi để truyền mẫu vào trong “synapse” đầu rạ

forward(inps);

m_pattern.setArray(outs); fireFwdPut(m_pattern);

Khi các phương thức “forward” và “fireFwdPut” được gọi, có ba biến liên quan đó là:

· Các trọng số kết nối · Độ lệch trọng số · Hàm ngưỡng

Phương thức đầu tiên được gọi là phương thức fireFwdGet. Công việc chính của phương thức này là nhận mẫu từ “synapse” đầu vào, và đợi cho đến khi không có mẫu nào được đưa vàọ

Lớp dữ liệu đầu vào chỉ nhận các giá trị {0,1} của mẫu để nhớ. Sau đó, lớp đầu vào sẽ áp dụng hàm ngưỡng, đưa chúng đến lớp tiếp theọ Chúng ta

sử dụng kiểu hàm sigmoid trong lớp dữ liệu đầu vào, thực thi hàm sigmoid qua lớp SigmoidLayer. Danh sách 2.2 chỉ ra phương thức SigmoidLayer.forward().

Danh sách 2.2: Phương thức SigmoidLayer.forward public void forward(double[] pattern)

{ int x; double in; int n = getRows(); try { for ( x = 0; x < n; ++x ) { in = pattern[x] + bias.value[x][0]; outs[x] = 1 / (1 + Math.exp(-in)); } (adsbygoogle = window.adsbygoogle || []).push({});

} catch ( Exception aioobe ) { aioobẹprintStackTrace(); }

Từ danh sách 2.2 ta thấy, phương thức SigmoidLayer.layer() áp dụng hàm sigmoid cho mỗi nơron trong lớp nàỵ Hàm sigmoid đã được đề cập ở trên.

Phương thức Layer.run đã xử lý dữ liệu đầu vào bằng cách sử dụng hàm ngưỡng sigmoid, lớp này sẵn sàng đưa mẫu tới lớp kế tiếp. Khi mẫu được đưa vào lớp tiếp theo, thì các trọng số thích hợp được cập nhật.

Bây giờ, phương thức Layer.run áp dụng hàm ngưỡng cho mỗi giá trị nơron, lớp phải truyền mẫu lên trên synapsẹ Synapse này áp các trọng số kết nối và gửi mẫu tới lớp tiếp theo, chúng được thể hiện ở phương thức fireFwdPut(). Phương thức fireFwdPut được chỉ ra trong danh sách 2.3.

Danh sách 2.3: Phương thức Layer.fireFwdPut protected void fireFwdPut(Pattern pattern) { if ( aOutputPatternListener == null ) { return;

};

int currentSize = aOutputPatternListener.size(); OutputPatternListener tempListener = null;

for ( int index = 0; index < currentSize; index++ ){ tempListener =

if ( tempListener != null ) {

tempListener.fwdPut((Pattern)pattern.clone()); };

}; }

Phương thức Layer.fireFwdPut chịu hai trách nhiệm. Thứ nhất, nó áp các trọng số kết nối giữa các nơron ở lớp hiện thời cho các nơron ở lớp tiếp theọ Thứ hai, nó chuyển mẫu này đến synapsẹ Phương thức này chuyển mẫu đến synapse bằng cách gọi phương thức SynapsẹfireFwdPut. Phương thức SynapsẹfireFwdPut được chỉ ra trong danh sách 2.4.

Danh sách 2.4: Phương thức SynapsẹfireFwdPut public synchronized void fwdPut(Pattern pattern) { if ( isEnabled() ) { count = pattern.getCount(); while ( items > 0 ) { try { wait(); } catch ( InterruptedException e ) { //ẹprintStackTrace(); return;

} } m_pattern = pattern; inps = (double[])pattern.getArray(); forward(inps); ++items; notifyAll(); } }

Khi vòng lặp chờ hoàn thành synapse, nó sẽ xử lý dữ liệu đầu vào, và sau đó truyền mẫu tới lớp tiếp theọ Việc xử lý chỉ là synapse sẽ thực hiện trên mẫu để áp độ lệch. Theo các quy trình của phương thức SynapsẹfwdPut, nó sẽ sao chép mẫu mà nó đã truyền vào biến lớp m_pattern.

m_pattern = pattern;

Mảng mẫu sau đó được sao chép tới một mảng hai giá trị để xử lý. inps = (double[])pattern.getArray();

Sau đó, mảng mẫu hai giá trị được truyền vào phương thức forward. Trong tất cả các lớp, phương thức forward luôn luôn được sử dụng để áp độ lệch.

Khi độ lệch đã được áp, mẫu được sẵn sàng được xử lý ở lớp tiếp theọ

Có nhiều loại synapse được tạo để có thể xử lý độ lệch theo các cách khác nhaụ Bây giờ, chúng ta sẽ xem xét phương thức FullSynapsẹforward

áp dụng độ lệch được chỉ ra trong danh sánh 2.5. Danh sách 2.5: Phương thức FullSynapsẹforward public void forward(double[] pattern) {

int x; double in; int n = getRows(); try { for ( x = 0; x < n; ++x ) { in = pattern[x] + bias.value[x][0]; outs[x] = 1 / (1 + Math.exp(-in)); }

} catch ( Exception aioobe ) { aioobẹprintStackTrace(); }

Ta có thể thấy độ lệch được áp cho mỗi phần tử của mẫu, và mẫu sẽ được thêm vào một độ lệch. Giá trị nghịch đảo của tổng này sẽ được trả về phương thức calling để được truyền tới lớp tiếp theọ Các quá trình này sẽ được lặp lại cho mỗi lớp của mạng nơron. Sau đây ta xem chúng sẽ thực thi lan truyền ngược trở lại như thế nàọ

2.3.2 Thực thi lan truyền ngược

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ố.

đượ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. (adsbygoogle = window.adsbygoogle || []).push({});

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 ) {

s += pattern[y] * arraỵvalue[x][y]; if ( getMomentum() < 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 ; (adsbygoogle = window.adsbygoogle || []).push({});

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++ ) {

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. (adsbygoogle = window.adsbygoogle || []).push({});

Để 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 ;

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 ;

}

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) ; (adsbygoogle = window.adsbygoogle || []).push({});

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.

Một phần của tài liệu Nhận dạng ký tự quang sử dụng mạng nơron kohonen (Trang 52)