Tải bản đầy đủ (.pdf) (22 trang)

Mạng Noron Nhân tạo

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (425.22 KB, 22 trang )

Mạng nơron nhân tạo
Phần 1. Con người và máy tính
Ngày nay không ai có thể phủ nhận vai trò cực kỳ quan trọng của máy tính trong
nghiên cứu khoa học kỹ thuật cũng như trong đời sống. Máy tính đã làm được những
điều kỳ diệu và giải được những vấn đề tưởng chừng nan giải. Càng ngày càng có
nhiều người tự hỏi, liệu máy tính đã thông minh hơn con người hay chưa? Chúng tôi
sẽ không trả lời câu hỏi ấy. Thay vào đó, chúng tôi sẽ nêu ra những khác biệt chủ yếu
giữa cách làm việc của máy tính và bộ óc con người.
Một máy tính, dù có mạnh đến đâu chăng nữa, đều phải làm việc theo một chương
trình chính xác đã được hoạch định trước bởi các chuyên gia. Bài toán càng phức tạp
thì việc lập trình càng công phu. Trong khi đó con người làm việc bằng cách học tập
và rèn luyện. Trong khi làm việc con người có khả năng liên tưởng, kết nối sự việc
này với sự việc khác, và quan trọng hơn hết, họ có thể sáng tạo.
Do có khả năng liên tưởng, con người có thể dễ dàng làm nhiều điều mà việc lập trình
cho máy tính đòi hỏi rất nhiều công sức. Chẳng hạn như việc nhận dạng hay trò chơi ô
chữ. Một em bé có thể tự học hỏi để nhận dạng và phân loại đồ vật chung quanh mình,
biết được cái gì là thức ăn, cái gì là đồ chơi. Một người bình thường cũng có thể đoán
được vài chữ trong một ô chữ. Nhưng thật khó mà dạy cho máy tính làm được những
việc ấy. Bạn hãy thử thiết kế một máy tính có khả năng làm như thế !
Từ lâu các nhà khoa học đã nhận thấy những ưu điểm ấy của bộ óc con người và tìm
cách bắt chước để thực hiện những máy tính có khả năng học tập, nhận dạng và phân
loại. Các mạng nơron nhân tạo (Artificial Neural Network, ANN) đã ra đời từ những
nỗ lực đó. ANN là một lãnh vực nghiên cứu rộng lớn và chỉ mới phát triển mạnh
khoảng 15 năm gần đây thôi. Tuy có nhiều kết quả khích lệ, nhưng ANN hãy còn xa
mới đạt được sự hoàn chỉnh như bộ óc con người.

Mạng nơron nhân tạo
Phần 2. Nơron và sự học tập


Sau đây là những thành phần chính trong cấu trúc của một nơron:










Soma là thân của nơron.
Các dendrites là các dây mảnh, dài, gắn liền với soma, chúng truyền dữ liệu
(dưới dạng xung điện thế) đến cho soma xử lý. Bên trong soma các dữ liệu đó
được tổng hợp lại. Có thể xem gần đúng sự tổng hợp ấy như là một phép lấy
tổng tất cả các dữ liệu mà nơron nhận được.
Một loại dây dẫn tín hiệu khác cũng gắn với soma là các axon. Khác với
dendrites, axons có khả năng phát các xung điện thế, chúng là các dây dẫn tín
hiệu từ nơron đi các nơi khác. Chỉ khi nào điện thế trong soma vượt quá một
giá trị ngưỡng nào đó (threshold) thì axon mới phát một xung điện thế, còn nếu
không thì nó ở trạng thái nghỉ.
Axon nối với các dendrites của các nơron khác thông qua những mối nối đặc
biệt gọi là synapse. Khi điện thế của synapse tăng lên do các xung phát ra từ
axon thì synapse sẽ nhả ra một số chất hoá học (neurotransmitters); các chất
này mở "cửa" trên dendrites để cho các ions truyền qua. Chính dòng ions này
làm thay đổi điện thế trên dendrites, tạo ra các xung dữ liệu lan truyền tới các
nơron khác.

Có thể tóm tắt hoạt động của một nơron như sau: nơron lấy tổng tất cả các điện thế
vào mà nó nhận được, và phát ra một xung điện thế nếu tổng ấy lớn hơn một ngưỡng
nào đó. Các nơron nối với nhau ở các synapses. Synapse được gọi là mạnh khi nó cho
phép truyền dẫn dễ dàng tín hiệu qua các nơron khác. Ngược lại, một synapse yếu sẽ
truyền dẫn tín hiệu rất khó khăn.



Các synapses đóng vai trò rất quan trọng trong sự học tập. Khi chúng ta học tập thì
hoạt động của các synapses được tăng cường, tạo nên nhiều liên kết mạnh giữa các
nơron. Có thể nói rằng người nào học càng giỏi thì càng có nhiều synapses và các
synapses ấy càng mạnh mẽ, hay nói cách khác, thì liên kết giữa các nơron càng nhiều,
càng nhạy bén. Hãy nhớ kỹ nguyên tắc này, vì chúng ta sẽ dùng nó trong việc học tập
của các ANNs.

Mạng nơron nhân tạo
Phần 3. Mô hình nơron
Mô hình McCulloch-Pitts (1943)
Sau đây là mô hình của một nơron nhân tạo:

Nơron này sẽ hoạt động như sau: giả sử có N inputs, nơron sẽ có N weights (trọng số)
tương ứng với N đường truyền inputs. Nơron sẽ lấy tổng cótrọng số của tất cả các
inputs. Nói như thế có nghĩa là nơron sẽ lấy input thứ nhất, nhân với weight trên
đường input thứ nhất, lấy input thứ hai nhân với weight của đường input thứ hai v.v...,
rồi lấy tổng của tất cả các kết quả thu được. Đường truyền nào có weight càng lớn thì
tín hiệu truyền qua đó càng lớn, như vậy có thể xem weight là đại lượng tương đương
với synapse trong nơron sinh học. Có thể viết kết quả lấy tổng của nơron như sau:


Kết quả này sẽ được so sánh với threshold t của nơron, nếu nó lớn hơn t thì nơron cho
output là 1, còn nếu nhỏ hơn thì output là 0. Ngoài ra ta cũng có thể trừ tổng nói trên
cho t, rồi so sánh kết quả thu được với 0, nếu kết quả là dương thì nơron cho ouput
bằng 1, nếu kết quả âm thì output là 0. Dưới dạng toán học ta có thể viết output của
nơron như sau:

Trong đó f là hàm Heaviside:


f được gọi là threshold function hay transfer function của nơron, còn giá trị (-t) còn
được gọi là bias hay offset của nơron.
Nếu chúng ta đưa thêm một input nữa vào, input thứ 0, có giá trị luôn luôn bằng 1 và
weight luôn luôn bằng bias (-t) thì output của nơron còn có thể viết dưới dạng:

Lưu ý là chỉ số của tổng bây giờ bắt đầu từ 0 chứ không phải bằng 1 như trước nữa.
Nếu các bạn đã quen lập trình, thì các bạn chắc cũng nhận xét là chúng ta có thể dễ
dàng viết một chương trình ngắn để mô phỏng hoạt động của nơron nhân tạo nói trên.
Các bạn có kiến thức về điện tử cũng có thể tạo một mạch đơn giản để thực hiện một
nơron nhân tạo.
Dạy nơron học như thế nào ?


Giả sử chúng ta muốn dạy nơron phân biệt chữ A và B. Khi đưa input là A chúng ta
muốn nơron cho output là 1, còn khi input là B thì nơron phải cho output bằng 0.

Hình ảnh của hai chữ A và B có thể phân tích thành nhiều ô nhỏ như sau:

Các bạn cũng thấy là trong ví dụ trên mỗi chữ gồm 5x10=50 ô, mỗi ô có thể có chứa
dấu x hay không chứa gì cả. Chúng ta có thể mã hóa một ô có chứa dấu x bằng số 1,
và một ô trống bằng số 0. Như vậy mỗi chữ được mã hóa bằng một dãy 50 số 1 và 0.
Nói cách khác, với mỗi chữ ta phải dùng 50 đường truyền để đưa 50 inputs là các số
0, 1 vào nơron.
Hãy bắt đầu bằng cách cho các weights những giá trị ngẫu nhiên, lúc này nơron chưa
biết gì hết. Bây giờ hãy input chữ A. Nơron sẽ lấy tổng có trọng số của các inputs và
so sánh kết quả với 0. Nếu kết quả dương thì output là 1, âm thì output là 0. Khả năng
nơron đoán đúng là 50%, vì các weights đang có giá trị hoàn toàn ngẫu nhiên. Nếu
nơron đoán đúng thì chúng ta không cần làm gì cả, nhưng khi nơron đoán sai (output
bằng 0), thì chúng ta phải tăng các weights của các inputs đang hoạt động (các inputs

khác không) lên, sao cho lần tới tổng có trọng số sẽ vượt quá threshold và tạo nên
output là 1.


Ngược lại, khi đưa chữ B vào và nơron đoán sai (output bằng 1), thì ta phải giảm các
weights của các inputs đang hoạt động xuống, sao cho lần tới tổng có trọng số sẽ nhỏ
hơn threshold và buộc nơron phải cho output bằng 0.
Như vậy, khi dạy chữ B thành công rồi thì nơron có quên đi chữ đã học trước đó là A
không ? Không, vì khi input là các chữ khác nhau thì nhóm các đường inputs đang
hoạt động cũng khác nhau hoặc là không hoàn toàn trùng nhau. Nhớ là chúng ta chỉ
biến đổi weights của các inputs đang hoạt động thôi. Chúng ta chỉ việc lập đi lập lại
quá trình dạy như trên cho tới khi nơron học thuộc bài mới thôi.
Phương pháp dạy vừa rồi được gọi là Hebbian Learning, vì nó được Donald Hebb đề
nghị năm 1949. Phương pháp dạy bằng cách biến đổi weights của các đường truyền
này có làm bạn liên tưởng tới sự tăng cường các synapses trong bộ óc con người
không ? Lưu ý là ta cũng có thể lập trình để thực hiện Hebbian Learning trên máy tính
một cách dễ dàng.

Mạng nơron nhân tạo
Phần 4. Perceptron
Trong phần này, chúng ta sẽ áp dụng ngay những kiến thức vừa rồi cho một mạng
nơron đơn giản là Perceptron, do Frank Rosenblatt đề nghị năm 1962. Perceptron là
một mạng chỉ có một lớp nơron (lớp này có thể có một hay nhiều nơron), có cấu trúc
như sau:

Tạo ra một Perceptron
Như đã nhận xét trong phần 3, chúng ta có thể lập trình để mô phỏng nơron nhân tạo
và việc dạy học cho nơron. Trong MatLab ta có thể thực hiện điều đó tương đối dễ
dàng. Trước hết hãy mở MatLab. Trong cửa sổ chính của MatLab (MatLab Command



Window) bạn chỉ cần gõ dòng lệnh sau đây để tạo một Perceptron tên là net, có một
nơron, và nhận input là một số có giá trị trong khoảng từ -1 đến 1:
net = newp([-1 1],1)
Trong MatLab [-1 1] là một ma trận một hàng và hai cột, có hai yếu tố là -1 và 1. Nếu
muốn tạo một Perceptron có một nơron, nhận input là một cặp số, số thứ nhất có giá
trị trong khoảng -1,1, và số thứ hai có giá trị trong khoảng 0,1, ta viết:
net = newp([-1 1;0 1],1)
Trong cách viết ma trận dấu ";" được dùng để tách các hàng, [-1 1;0 1] là một ma trận
có hai hàng và hai cột, hàng thứ nhất chứa min, max của thành phần input thứ nhất,
hàng thứ hai chứa min, max của thành phần input thứ hai:
-1 1
0 1

Sau khi bạn kết thúc dòng lệnh và gõ Enter, MatLab sẽ tạo ra Perceptron, gán nó cho
biến net và biểu thị các tính chất và biến số của mạng net. Tạm thời chúng ta không
quan tâm tới các tính chất và biến số đó. Để MatLab không biểu thị chúng ra, ta chỉ
cần thêm dấu ";" vào cuối dòng lệnh. Ví dụ, dòng lệnh sau đây sẽ tạo ra một
perceptron có hai nơron, nhận input là một bộ ba số, mỗi số có min, max trong khoảng
từ -10 đến 10, mà không biểu thị kết quả:
net = newp([-10 10;-10 10;-10 10],2);
Ngoài ra bạn có thể dùng ";" ở sau bất kỳ dòng lệnh nào trong MatLab để MatLab
không biểu thị kết quả của dòng lệnh ấy ra. Trước khi tiếp tục các bạn hãy xóa biến
net ra khỏi bộ nhớ, muốn thế hãy gõ:
clear net
Còn nếu muốn xóa tất cả các biến đang tồn tại trong bộ nhớ của MatLab, hãy dùng:
clear
Để xóa màn hình của Command Window bạn dùng:
clc
Khi bạn viết sai một câu lệnh và gõ Enter thì MatLab sẽ báo lỗi ngay. Tuy nhiên bạn

không thể đưa con trỏ vào câu lệnh đó để sửa chữa đâu. Bạn phải nhấn nút mũi tên lên


trên bàn phím để MatLab hiển thị lần lượt các câu lệnh đã gõ trước đó. Khi tới câu
lệnh cần sửa đổi thì bạn ngừng lại để sửa.
Cho Perceptron hoạt động
Bây giờ trong MatLab Command Window các bạn hãy tạo một Perceptron có một
nơron, nhận input là một số thay đổi trong khoảng từ -100 tới 100, và đặt tên cho nó là
Bi:
Bi = newp([-100 100],1);
Perceptron Bi đang có các weights ngẫu nhiên, nó chưa có một kiến thức nào đặc biệt
cả. Hãy đưa một số nào đó trong khoảng -100, 100, số 97 chẳng hạn, và xem nó cho
output là gì:
y = sim(Bi,97)
Trong dòng lệnh vừa rồi, các bạn vừa bảo Bi nhận diện con số 97 bằng lệnh
sim(Bi,97), gán output của Bi cho biến số y, và biểu thị y ra màn hình. Hãy thử bảo Bi
nhận diện một số inputs khác xem sao.
Dạy Bi phân biệt các số âm, dương
Bây giờ chúng ta hãy dạy Bi phân biệt các số âm, dương trong khoảng -100, 100.
Muốn thế trước hết ta chọn một bộ các số âm, dương làm ví dụ để dạy, chẳng hạn như
bộ gồm hai số -2 và 65 sau đây:
examples = {-2 65}
Chúng ta muốn khi đưa một số âm thì Bi phải trả lời là 0, còn khi đưa một số dương
thì nó phải trả lời là 1. Vì vậy ta cũng phải soạn một bộ lời giải tương ứng để dạy cho
Bi. Trong trường hợp này bộ lời giải sẽ gồm hai số 0 và 1, lời giải 0 tương ứng với ví
dụ -2 và lời giải 1 tương ứng với ví dụ 65:
answers = {0 1}
Để dạy cho Bi bộ ví dụ trên ta gõ như sau:
Bi = train(Bi,examples,answers);
Hàm train sẽ dùng bộ ví dụ examples và bộ lời giải answers để dạy cho Bi. Bi sẽ được

dạy bộ ví dụ đó nhiều lần nếu cần thiết, cho tới chừng nào nó hết nhầm lẫn mới thôi.
Trong MatLab mỗi vòng dạy qua tất cả các ví dụ được gọi là một epoch, còn sự nhầm


lẫn của Bi được ước lượng bằng một đại lượng ký hiệu là MAE (Mean Average
Error).
Bây giờ Bi đã học bài xong, ta hãy thử tài của nó xem sao. Trước hết hãy xem nó có
thuộc bài hay không bằng cách bảo nó phân loại trở lại các số trong bộ ví dụ:
y = sim(Bi,examples)
Cuối cùng hãy thử tài suy đoán của Bi bằng cách bảo nó phân loại các số không nằm
trong bài học:
tests = {-78 45 35.3 -24.6 pi 0}
y = sim(Bi,tests)
Các bạn sẽ thấy Bi phân loại được hết các số vừa rồi, chỉ trừ trường hợp số 0, nó cho
rằng 0 là số âm ! Nhưng tôi không trách nó đâu, vì số 0 là một số đặc biệt.
Bi học lý luận
Chắc các bạn đều biết phép toán logic OR, chúng ta sẽ dạy cho Bi phép toán đó. Cho
một cặp số nằm trong khoảng 0,1, kết quả của phép toán OR được cho trong bảng sau:
OR

0

1

0

0

1


1

1

1

Bộ ví dụ sẽ gồm 4 cặp số, mỗi cặp được cho dưới dạng một ma trận hai hàng, một cột.
Do đó trước hết Bi phải được trang bị để có thể đọc các inputs gồm 2 số. Ngoài ra, bộ
lời giải sẽ gồm 4 số 0,1 tương ứng với từng trường hợp trong bộ ví dụ. Ta gõ lần lượt
các dòng lệnh sau:
clear
clc
Bi = newp([0 1;0 1],1);
examples = {[0;0] [0;1] [1;0] [1;1]}
answers = {0 1 1 1}
Bạn hãy dạy cho Bi bằng bộ ví dụ và bộ lời giải trên. Sau đó hãy ôn lại bài và thử tài
suy đoán của Bi bằng cách làm nhiễu các inputs một chút như sau:


tests = {[0.01;0] [0;0.96] [1;0.01] [0.95;0.96]}
Một bài học lý luận khác
Bi giỏi quá phải không các bạn ? Các bạn hãy thử dạy Bi phép toán logic XOR
(Exclusive OR) xem sao. Sau đây là bảng giá trị của XOR:
XOR

0

1

0


0

1

1

1

0

Nếu cần bạn có thể tăng số vòng dạy (epochs) lên, để ấn định số epochs là 500 chẳng
hạn bạn dùng dòng lệnh sau:
Bi.trainParam.epochs = 500;
Nếu bạn đã quen với cách lập trình định hướng đối tượng (object-oriented) thì có lẽ
cách viết như trên rất quen thuộc với bạn. Bi là một Perceptron, các thông số dạy học
trainParam là một thuộc tính của Bi, vì vậy để xem xét các thông số ấy ta viết:
Bi.trainParam
Số các vòng dạy epochs lại là một yếu tố trong các thông số dạy học trainParam, vì
thế để biết giá trị của epochs ta dùng dấu "." thêm một lần nữa:
Bi.trainParam.epochs
Các bạn hãy xem câu trả lời của bài tập này ở phần kế tiếp.

Mạng nơron nhân tạo
Phần 5. Mạng Feedforward
Thật ra mạng Perceptron không thể học được bài toán XOR, hạn chế ấy là do chính
cấu trúc của mạng. Vì thế năm 1986 Rumelhart và McClelland đã cải tiến Perceptron
thành mạng Perceptron nhiều lớp (MultiLayer Perceptron, MLP), hay còn gọi là mạng
Feedforward.



Mạng Feedforward là một mạng gồm một hay nhiều lớp nơron, trong đó các dây dẫn
tín hiệu chỉ truyền theo một chiều từ input qua các lớp, cho đến output. Sau đây là một
ví dụ gồm hai lớp, mỗi lớp có hai nơron:

Lưu ý là lớp input không được coi là một lớp của mạng. Ngoài ra mạng Feedforward
cũng khác Perceptron ở chỗ là nó không dùng hàm Heaviside làm transfer function
nữa, thay vào đó là các hàm sigmoid (tansig hay logsig). Sau đây là dạng của hàm
tansig (tanh(x/2)):

Tạo ra một mạng Feedforward
Để tạo một mạng Feedforward tên là net, nhận input là các cặp số trong khoảng 0,1,
có hai lớp, mỗi lớp có một nơron, ta gõ dòng lệnh sau:
net = newff([0 1;0 1],[1 1]);
Ma trận thứ nhất trong hai arguments của newff cũng giống như trong trường hợp
Perceptron, là ma trận chứa các giá trị min,max của hai thành phần input. Còn ma trận
thứ hai, [1 1], thì chứa số nơron trong mỗi lớp; ở đây ma trận có hai cột vì mạng có
hai lớp, cà hai yếu tố của ma trận đều bằng 1, vì mỗi lớp chỉ có chứa một nơron thôi.
Sau đây là cấu trúc của net:


Ngoài những tính chất xác định bởi các arguments trong lệnh newff, thì các tính chất
còn lại của mạng đều được cho trước một cách mặc nhiên (default properties). Nhưng
chúng ta cũng có thể thay đổi các tính chất ấy khi cần thiết. Transfer function mặc
nhiên của tất cả các lớp đều có dạng tansig, để dùng logsig cho lớp thứ nhất và tansig
cho lớp thứ hai ta dùng:
net = newff([0 1;0 1],[1 1],{'logsig' 'tansig'});
Nếu dùng logsig làm transfer function cho cả hai lớp ta viết:
net = newff([0 1;0 1],[1 1],{'logsig' 'logsig'});
Phương pháp dạy học mặc nhiên của một mạng Feedforward là trainlm, ngoài ra còn

có trainbfg và trainrp. Tuỳ theo nội dung bài học, các phương pháp sau có thể hiệu
quả hơn. Để dùng trainbfg hay trainrp làm phương pháp giảng dạy ta thêm một
argument thứ tư vào dòng lệnh vừa rồi:
net = newff([0 1;0 1],[1 1],{'logsig' 'logsig'},'trainbfg');
net = newff([0 1;0 1],[1 1],{'logsig' 'logsig'},'trainrp');
Thay đổi tính chất của mạng
Transfer function và phương pháp dạy học là hai thuộc tính duy nhất có thể thay đổi
bằng cách thêm argument vào hàm newff. Để thay đổi các thuộc tính còn lại ta phải
viết lệnh gán trực tiếp, tương tự như khi thay đổi số epochs dạy của Perceptron vậy.
Trong nhóm các thông số dạy học, ngoài epochs ra còn có các thông số thông dụng
sau:



goal: là sai số mà ta muốn mạng đạt được khi học tập, goal có giá trị mặc nhiên
là 0.
min_grad: là gradient nhỏ nhất của sai số khi học tập, min_grad có giá trị mặc
nhiên là 1e-6.


Để đổi goal và min_grad của mạng thành 1e-12 ta dùng:
net.trainParam.goal = 1e-12;
net.trainParam.min_grad = 1e-12;
Ngoài ra còn có hai thuộc tính quan trọng khác là inputConnect và layerConnect;
inputConnect quy định các đường truyền từ input tới các lớp, còn layerConnect quy
định các đường truyền giữa các lớp của mạng. Cả hai thuộc tính này đều là các ma
trận.
Trong trường hợp này inputConnect là một ma trận hai hàng một cột, [1;0], mỗi hàng
ứng với một lớp. Yếu tố thứ nhất bằng 1, có nghĩa là input được nối trực tiếp với lớp
thứ nhất, yếu tố thứ hai bằng 0, có nghĩa là input không được nối trực tiếp với lớp thứ

hai. Để cả hai lớp của net đều được nối trực tiếp với input ta viết dòng lệnh sau:
net.inputConnect = [1;1];
Còn layerConnect là một ma trận có hai hàng hai cột, [0 0;1 0] (nếu số lớp nơron là n
thì đây là một ma trận nxn). Yếu tố (i,j) của ma trận này, ký hiệu là layerConnect(i,j)
chỉ lấy một trong hai giá trị là 0 hay 1; layerConnect(i,j) bằng:



0 khi không có đường truyền từ lớp j tới lớp i;
1 khi có đường truyền từ lớp j tới lớp i.

Bài tập: Phép toán XOR
Các bạn hãy thiết kế một mạng Feedforward có thể học phép toán XOR. Nhớ là không
cần dùng quá 3 lớp nơron, vì Kolmogorov đã chứng minh rằng một mạng 3 lớp đã có
thể thực hiện được bất kỳ bài toán phân loại nào. Rất tiếc là Kolmogorov không thể
cho chúng ta biết mỗi lớp phải có bao nhiêu nơron. Đây là một chỉ dẫn: hãy đi từ đơn
giản tới phức tạp, đừng bắt tay vào xây dựng ngay những mạng có nhiều nơron, hãy
bắt đầu từ 1 hay 2 lớp, mỗi lớp có 1 hay 2 nơron; ngoài ra hãy chú ý tới liên kết giữa
input và các lớp trong mạng.

Mạng nơron nhân tạo
Phần 5. Mạng Feedforward (tiếp theo)
Bài tập: Nhận dạng chữ số


Sau đây là một bộ các chữ số từ 0 đến 9:

Mỗi con số trên là một ma trận 7x5, có các yếu tố là ký tự 'o' hay dấu trống ' '. Mỗi ma
trận có thể coi như tương đương với một ảnh có 7x5 pixels.
Các bạn hãy thiết kế một mạng Feedforward và dạy cho nó đọc các chữ số trên.

Tất nhiên, trước hết chúng ta sẽ phải xây dựng một bộ ví dụ và một bộ lời giải. Để các
bạn có thể tập trung vào việc thiết kế mạng, tôi sẽ giúp các bạn xây dựng bộ ví dụ và
lời giải.
Dưới đây một chút các bạn sẽ thấy một liên kết tới một file tên là digits.m, tôi muốn
các bạn save file này vào folder làm việc của bạn. Nhưng trước hết bạn hãy đọc kỹ
hướng dẫn sau đây.
Để chép digits.m hãy click vào liên kết và chọn Save it to disk trong dialog sẽ xuất
hiện sau đó, rồi browse tới folder làm việc của bạn và click Save. Cũng có thể là
Internet Explorer đã được setup để mở file digits.m mà không hỏi xem bạn có muốn
save nó hay không. Trong trường hợp đó hãy right-click vào liên kết và chọn Save
Target As trong popup menu sẽ xuất hiện sau đó. Chú ý là tùy theo version của
Internet Explorer, các mục bằng tiếng Anh vừa rồi có thể khác nhau đôi chút.
--> digits.m
Digits.m là một script file của MatLab (script file: file có chứa các dòng lệnh
MatLab). Trong digits.m có các lệnh để tạo ra các ma trận trong bảng nói trên, và để
xây dựng bộ ví dụ và bộ lời giải.
Để xem nội dung của digits.m, hãy click nút
trên toolbar của MatLab Command
Window. Sau đó browse tới thư mục chứa digits.m (thư mục làm việc của bạn), chọn
chọn file digits.m, click Open. File digits.m sẽ được mở ra trong một cửa sổ khác của
MatLab, gọi là MatLab Editor.
Trong digits.m các ma trận ký tự trong bảng trên được đặt tên là one, two, three, ...,
cho tới nine, còn tập hợp các ma trận đó thì được đặt tên là digits.


Để dạy cho mạng đọc các số vừa rồi, chúng ta tạo một bộ ví dụ gồm chính các số ấy.
Để dùng lệnh train ta phải biến đổi các ma trận 7x5 nói trên thành các ma trận chỉ có
một cột và 35 hàng. Muốn thế, chỉ cần sắp xếp lần lượt các hàng của mỗi ma trận 7x5
vào một ma trận cột. Ngoài ra ta cũng phải thay thế các ký tự 'o' và ' ' bằng các số, vì
input của mạng nơron phải là các số. Tôi quy ước dùng số 1 để thay 'o' và số -1 để

thay ' '. Bộ ví dụ xây dựng như thế được đặt tên là bitmaps. Như vậy mỗi thành phần
của bitmaps, ký hiệu là bitmaps{i}, với i = 1, 2, ..., 10, tương ứng với ảnh của một số
từ 0 cho tới 9.
Cuối cùng trong digits.m ta xây dựng bộ lời giải, đặt tên là answers, gồm các lời giải
answers{i}, i = 1, 2, ..., 10. Để phân biệt 10 câu trả lời khác nhau, tôi dùng một ma
trận cột (10x1) cho mỗi câu trả lời. Trong câu trả lời thứ i chỉ có yếu tố thứ i bằng 1,
tất cả các yếu tố còn lại đều bằng -1.
Các bạn có thể chép một hay nhiều dòng lệnh trong digits.m từ MatLab Editor qua
MatLab Command Window và thực hiện chúng. Hay đơn giản hơn, bạn hãy vào File,
chọn Set Path. Trong dialog box sẽ xuất hiện sau đó, bạn nhấn nút Add Folder. Sau đó
bạn chọn folder làm việc của mình và nhấn OK. Như vậy MatLab sẽ thêm folder làm
việc của bạn vào danh mục những nơi cần tìm script files. Bây giờ nếu bạn viết:
digits
Thì sau khi gõ Enter MatLab sẽ thực hiện toàn bộ các lệnh trong digits.m, tạo ra bộ ví
dụ bitmaps và bộ lời giải answers dùm bạn. Để xem một chữ số, ví dụ hay lời giải nào
đó (chữ số, ví dụ và lời giải thứ 4 chẳng hạn), bạn viết tiếp các dòng sau:
digits{4}
bitmaps{4}
answers{4}
bitmaps{4} là một ma trận có 35 hàng, có thể bất tiện cho bạn khi xem. Bạn có thể
xem dạng chuyển vị của nó như sau:
bitmaps{4}'
Trong MatLab, ký tự ' được đặt sau một ma trận để biến đổi nó thành ma trận chuyển
vị.
Tới đây bạn đã tạo xong bộ ví dụ bitmaps, bộ lời giải answers và đã làm quen với
dạng ma trận của chúng. Hãy bắt đầu xây dựng mạng và dạy nó đi !


Bạn có thể xây dựng ngay một mạng có ba lớp, chú ý là lớp thứ ba (output) phải có 10
nơron, vì mỗi lời giải là một bộ gồm 10 số. Đối với các lớp 1 và 2 bạn có thể dùng từ

10 nơron trở lên.
Ngoài ra argument đầu tiên của newff (xác định min, max của các inputs) là một ma
trận 35x2, vì có 35 số cho mỗi input. Để tránh gõ một dòng quá dài, bạn có thể tạo ra
ma trận ấy như sau (bỏ qua các các dòng bắt đầu bằng ký tự %, vì chúng là các chú
thích):
% tao mot ma tran don vi co 35 hang, 2 cot, gan cho bien range
range = ones(35,2);
% cot thu nhat cua range bang -1
range(:,1) = -1;
và sau đó dùng range làm argument thứ nhất trong newff.
Nếu thành công, bạn đã thiết kế một mạng có thể đọc được chữ viết của tôi rồi
đó (tạm thời chỉ là các chữ số từ 0 tới 9 thôi). Tương tự như vậy, bạn có thể mở
rộng mạng, bổ sung bộ ví dụ và lời giải để nó đọc được bộ chữ cái, thậm chí đọc được
chữ viết tay của nhiều người khác nhau !

Mạng nơron nhân tạo
Phần 6. Mạng Hopfield
Các bạn hãy quan sát kỹ 5 mẫu trang trí sau đây:

Và đây là 5 mẫu khác nữa:


Trông các mẫu sau này có vẻ quen quen phải không các bạn? Đúng vậy, vì thật ra
chúng là ảnh bị nhiễu của bộ mẫu trang trí đầu tiên. Bạn có thể cho biết mẫu nào
tương ứng với mẫu nào không? Nhưng thôi, hãy khoan nói ra đã, chút nữa các bạn sẽ
có thể so sánh ý kiến của mình với ý kiến của mạng Hopfield.
Mạng Hopfield do John Hopfield đề nghị năm 1980. Về mặt cấu trúc thì mạng
Hopfield là mạng chỉ có một lớp nơron, trong đó mỗi nơron đều nối với tất cả các
nơron còn lại:


Chắc các bạn cũng nhận xét ngay là mạng trên không có lớp input và output như các
mạng đã tìm hiểu trước đây. Lớp nơron duy nhất của mạng cũng chính là nơi nhận
input và và phát output. Như vậy số nơron trong mạng phải bằng số thành phần input
cũng như số thành phần output.
Ngoài ra mạng Hopfield chỉ nhận các inputs là -1 hay 1. Transfer function của mạng
là hàm satlins:


Tạo ra một mạng Hopfield
Ngay khi tạo một mạng Hopfield ta phải cho mạng biết bộ ví dụ dưới dạng một ma
trận, trong đó mỗi cột là một ví dụ. Trong ví dụ sau đây T là một bộ gồm hai ví dụ,
mỗi ví dụ là một bộ ba số, cụ thể là {-1 -1 1} và {1 -1 1}. Vì mỗi input có 3 thành
phần nên mạng Hopfield mang tên net sẽ có 3 nơron:
T = [-1 -1 1; 1 -1 1]'
net = newhop(T);
Các mạng như Perceptron và Feedforward khi mới được tạo ra thì có weights ngẫu
nhiên. Còn trong mạng Hopfield thì các weights ban đầu được xác định theo bộ ví dụ
mà ta cung cấp. Giả sử có M ví dụ, mỗi ví dụ có N thành phần (như vậy số nơron
trong mạng cũng là N), quy luật xác định các weights của mạng Hopfield là như sau:

Trong đó w(i,j) là weight của đường truyền giữa hai nơron i và j, còn x(s,j) là thành
phần thứ j của ví dụ thứ s. Sau khi xem xét và hiểu công thức trên thì chúng ta có thể
tạm thời quên nó đi, vì ở đây hàm newhop sẽ xác định các weights ban đầu theo công
thức đó dùm chúng ta.
Như vậy có thể nói khi được hình thành, mạng Hopfield đã ghi nhớ luôn những ví dụ
mà ta cung cấp. Mạng Hopfield làm điều đó theo cách riêng của nó, bằng cách ghi
thông tin về bộ ví dụ trong các weights.


Trở lại với các mẫu trang trí ở đầu bài này. Chúng ta sẽ tạo một mạng Hopfield và đưa

bộ mẫu trang trí ấy vào bộ nhớ của mạng. Trước hết, cũng giống như đối với bộ chữ
số trong phần trước, chúng ta sẽ biến đổi chúng thành dạng thích hợp để mạng có thể
hiểu được.
Mỗi mẫu trên là một ma trận 10x10 gồm các yếu tố là ký tự 'O' và dấu trống ' ', có thể
coi nó như một ảnh có 10x10 pixels. Cần phải biến đổi những ảnh này thành các ma
trận cột có 100 dòng, với các yếu tố là các số -1 và 1; 1 tương ứng với ký tự 'O' và -1
tương ứng với dấu trống ' '. Sau đó ghép tất cả các ma trận cột lại thành ma trận chứa
bộ ví dụ.
Tôi đã viết một script file, images.m, để xây dựng ma trận ví dụ ấy. Các bạn hãy save
file này vào folder làm việc của mình. Sau đó viết những dòng lệnh sau:
% tao bo vi du bitmaps
images
% tao mang net (co 100 noron) va dua bitmaps vao bo nho cua net
net = newhop(bitmaps);
Trong images.m, bộ mẫu trang trí đầu tiên được gọi là images, và bộ ví dụ xây dựng
từ chúng được gọi là bitmaps. Để xem một mẫu hay ví dụ các bạn gọi trực tiếp các
thành phần images{i} của bộ mẫu images hay các cột bitmaps(:,i) của ma trận
bitmaps. Đừng quên rằng bitmaps(:,i) là những ma trận cột có tới 100 hàng !
% anh thu ba trong bo anh images
images{3}
% chuyen vi cua cot thu ba (vi du thu ba) trong bo vi du bitmaps
bitmaps(:,3)'
Cho mạng Hopfield hoạt động
Tới đây chúng ta có thể cho mạng Hopfield làm việc mà không cần phải dạy nó. Cách
thức làm việc của mạng Hopfield là như thế này:




Đưa một mẫu mới, không nằm trong bộ ví dụ, cho mạng xem. Trong trường

hợp này, đó là một mẫu đã bị nhiễu làm cho biến dạng trong bộ mẫu thứ hai
trên đây.
Để cho mạng có thời gian suy nghĩ. Mạng sẽ thực hiện một hay nhiều bước lặp
cho tới khi suy nghĩ của nó ổn định, không thay đổi nữa. Mỗi bước lặp như vậy
được gọi là một timestep. Tại mỗi timestep, mạng nhận output của timestep
trước đó làm input rồi cho ra một ouput, output này sẽ được dùng làm input cho
timestep tiếp theo ... Cứ thế cho tới khi output của mạng không thay đổi nữa.




Thông thường ouput cuối cùng của mạng Hopfield chính là ảnh nguyên vẹn của
mẫu khi chưa bị nhiễu. Như vậy mạng có khả năng khôi phục lại một ảnh đã bị
nhiễu làm cho biến dạng.

Script file images.m cũng biến đổi luôn bộ mẫu thứ hai thành các bitmaps mà mạng
Hopfield có thể đọc được. Bộ mẫu ấy được đặt tên là images_1, còn các mẫu dưới
dạng ma trận là bitmaps_1.
Để đưa ảnh thứ hai trong bộ mẫu bị nhiễu cho mạng net xem xét, dành 10 timesteps
cho mạng suy nghĩ, sau đó ghi lại output của tất cả các timesteps trong biến y, ta viết:
y = sim(net,{1 10},{},{bitmaps_1(:,2)});
Trong lệnh trên thì argument thứ nhất của sim là tên của mạng (net); argument thứ hai
là một bộ hai số, số thứ nhất là số mẫu mà ta đưa cho mạng nhận dạng, trong trường
hợp này là 1 vì ta chỉ đưa một mẫu, còn số thứ hai là số timesteps cho mạng làm việc
(nếu output của mạng vẫn chưa ổn định sau 10 timesteps thì bạn phải tăng số này lên);
argument thứ ba, gọi là Final Input Delay Conditions, được để trống, vì mạng
Hopfield không dùng argument này; và argument cuối cùng là input, được cho dưới
dạng một ma trận cột, nhớ đặt nó trong các ngoặc nhọn {} !
Suy nghĩ của mạng tại từng timestep
Để xem xét output y{i} của mạng tại từng timestep trước hết chúng ta cần một hàm để

biến đổi y{i} thành các ảnh, vì y{i} là các ma trận cột chỉ chứa 1 và -1. Tôi có viết
một hàm như vậy, gọi là img, trong script file img.m. Các bạn hãy save img.m vào
folder làm việc của mình, sau đó viết dòng lệnh sau:
% vong lap 10 buoc de bien doi y{i} thanh anh z{i}
for i=1:10 z{i}=img(y{i}); end
% xem output cua timestep 1,2, ...
z{1}
z{2}
% ... tiep tuc den timestep thu 10
z{9}
z{10}
Trong trường hợp đang xem thì tôi thấy output của mạng ổn định sau 6 timesteps, các
bạn hãy xem sau đây input ban đầu và sự khôi phục dần dần của ảnh qua 6 timesteps:


Các bạn thấy thế nào? Hãy tiếp tục nhờ mạng nhận dạng các ảnh bị nhiễu còn lại và so
sánh với nhận định ban đầu của bạn về các ảnh đó.
Bài tập:
Bạn có thấy ba bộ mặt trên đây không? Đó là smiley, frowny và dead, rất thông dụng
trong các trang Web. Chúng là các ảnh 15x15 pixels, sau đây là ảnh phóng to của
chúng:

Hãy tạo một mạng Hopfield và lưu trữ các ảnh trên vào mạng. Sau đó nhờ mạng nhận
diện các ảnh bị nhiễu của chúng. Các bạn có thể tự tạo ra các ảnh bị nhiễu ở mức độ
khác nhau để thử nghiệm khả năng khôi phục ảnh của mạng Hopfield, hay dùng các
ảnh sau đây:


Thay lời kết
Để kết thúc phần này, chúng tôi lưu ý là mạng Hopfield chỉ có thể khôi phục thành

công các ảnh nếu số ví dụ mà bạn lưu trữ trong mạng không vượt quá 0.15N, với N là
số nơron.
Ngoài ra nên nhớ rằng N cũng chính là số thành phần của một input. Chẳng hạn, nếu
bạn lưu trữ ảnh của 10 con số từ 0 tới 9 trong phần trước, thì việc khôi phục ảnh bị
nhiễu của chúng sẽ không thành công lắm, vì mạng sẽ chỉ có N = 35 nơron thôi (ứng
với 35 pixels của một ảnh). Với kích thước của mạng như vậy, chỉ nên lưu trữ khoảng
0.15x35, tức là 5 ảnh mà thôi.
Mặt khác chúng ta cũng có thể tăng số nơron lên bằng cách tăng kích thước của các
ảnh lên. Nếu ảnh có 10x10 pixels thay vì 7x5 pixels thì số nơron sẽ tăng từ 35 lên 100,
do đó mạng có thể lưu trữ được 15 ảnh và khôi phục ảnh bị nhiễu của chúng.



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Tải bản đầy đủ ngay
×