Machine Learning thật thú vị (Phần 2: ví dụ đơn giản Neuron Network)

Tôi thích học những thứ mà tôi thích chơi, và tôi thấy nó mang lại kết quả tốt. Hướng dẫn này dạy backpropagation thông qua một ví dụ trò chơi rất đơn giản, thực hiện bằng một đoạn code python ngắn, và bạn sẽ thấy nó quen quen.

Bài toán

Chúng ta sẽ tạo ra một Neuron Network để dự đoán kết quả như dưới đây:

Input 1 Input 2 Output
0 0 0
1 1 0
1 0 1
0 1 1

Code

Bạn thấy nó "quen quen" chứ? Đó chính là phép XOR nhỉ? Ok, không chần chừ gì nữa, chúng ta bắt tay vào code mạng Neuron Network này nào. Tôi sẽ dùng mạng Neuron 2 layer

import numpy as np

# sigmoid function
def nonlin(x,deriv=False):
    if(deriv==True):
        return x*(1-x)
    return 1/(1+np.exp(-x))
    
# input dataset
X = np.array([  [0,0],
                [1,1],
                [1,0],
                [0,1] ])
    
# output dataset            
y = np.array([[0,0,1,1]]).T

# seed random numbers to make calculation
# deterministic (just a good practice)
np.random.seed(1)

# initialize weights randomly with mean 0
syn0 = 2*np.random.random((2,1)) - 1

for iter in xrange(10000):

    # forward propagation
    l0 = X
    l1 = nonlin(np.dot(l0,syn0))

    # how much did we miss?
    l1_error = y - l1

    # multiply how much we missed by the 
    # slope of the sigmoid at the values in l1
    l1_delta = l1_error * nonlin(l1,True)

    # update weights
    syn0 += np.dot(l0.T,l1_delta)

print "Output After Training:"
print l1

Kết quả

Kết quả sau khi Training:
[[ 0.00966449]
 [ 0.00786506]
 [ 0.99358898]
 [ 0.99211957]]

Giải thích các biến

Biến Mô tả
X Nhập dữ liệu ma trận đầu vào, nơi mỗi hàng là một ví dụ để training
Y Ma trận dữ liệu đầu ra, nơi mỗi hàng là một ví dụ training tương ứng
L0 Lớp đầu tiên của Neuron Network, được xác định bởi dữ liệu đầu vào
L1 Lớp thứ hai của Neuron Network, còn được gọi là lớp ẩn
Syn0 Lớp trọng lượng thứ nhất, Synapse 0, kết nối từ L0 đến L1.
* Toán tử nhân, vì vậy hai vectơ có kích thước bằng nhau nhân các giá trị tương ứng 1 đến 1 để tạo ra một vector cuối cùng có kích thước giống hệt nhau.
- Các phép trừ nguyên tố, vì vậy hai vectơ có kích thước bằng nhau sẽ trừ đi các giá trị tương ứng 1 đến 1 để tạo ra một vector cuối cùng có kích thước giống hệt nhau.
x.dot(y) Nếu x và y là vectơ, đây là một dấu chấm. Nếu cả hai đều là ma trận, đó là phép nhân ma trận. Nếu chỉ có một là một ma trận, thì đó là phép nhân ma trận vector.

Giải thích code

Như bạn thấy trong kết quả sau khi Training, nó đã hoạt động! Trước khi tôi mô tả quá trình, tôi khuyên bạn nên "nghịch" xung quanh với mã để có được một cảm giác trực quan cho nó hoạt động như thế nào. Dưới đây là một số cách mà bạn có thể nghịch:

  • So sánh l1 sau lần lặp đầu tiên và sau lần lặp cuối cùng.
  • Kiểm tra function "nonlin". Đây là những gì cho chúng ta một xác suất như đầu ra.
  • Kiểm tra xem l1_error thay đổi như bạn lặp lại như thế nào.
  • Lấy ra dòng 36. Phần lớn của điều kỳ diệu nằm ở đây.
  • Kiểm tra dòng 39. Tất cả mọi thứ trong mạng chuẩn bị cho hoạt động này.

Bây giờ chúng ta sẽ nghiên cứu từng dòng code nhé.

Khuyến nghị: bạn hãy mở 2 màn hình, 1 là của đoạn code trên, 1 là đoạn dưới đây để bạn có thể dễ dàng theo dõi.

Dòng 01: Đây là numpy, là một thư viện đại số tuyến tính. Đây là sự phụ thuộc duy nhất của chúng tôi.

Dòng 04: Đây là "phi tuyến tính" của chúng ta. Mặc dù nó có thể là một số loại function, tính không tuyến tính này mô tả một hàm gọi là "sigmoid". Một hàm sigmoid ánh xạ bất kỳ giá trị nào với một giá trị giữa 0 và 1. Chúng ta sử dụng nó để chuyển đổi các số thành xác suất. Nó cũng có một số đặc tính mong muốn khác để huấn luyện các Neuron Network.

Dòng 05: Lưu ý rằng function này cũng có thể tạo ra các dẫn xuất của một sigmaoid (khi deriv là True). Một trong những tính chất mong muốn của một hàm sigmoid là đầu ra của nó có thể được sử dụng để tạo ra các dẫn xuất của nó. Nếu đầu ra của sigmoid là một biến output, sau đó các đạo hàm chỉ đơn giản là ra * (1-out). Điều này rất hiệu quả.

Nếu bạn không quen với các dẫn xuất, chỉ cần suy nghĩ về nó như là độ dốc của hàm sigmoid tại một điểm nhất định (như bạn có thể thấy ở trên, các điểm khác nhau có độ dốc khác nhau).

Dòng 10: Điều này khởi tạo bộ dữ liệu đầu vào của chúng ta như một ma trận numpy. Mỗi hàng là một ví dụ để training. Mỗi cột tương ứng với một trong các node đầu vào của chúng ta. Vì vậy, chúng tôi có 2 node đầu vào mạng và 4 ví dụ training.

Dòng 16: Điều này khởi tạo bộ dữ liệu đầu ra của chúng tôi. Trong trường hợp này, tôi tạo ra các tập dữ liệu theo chiều ngang (với một hàng và 4 cột) cho không gian. ".T" là chức năng chuyển vị. Sau khi chuyển đổi, ma trận Y này có 4 hàng với một cột. Giống như đầu vào của chúng tôi, mỗi hàng là một ví dụ training, và mỗi cột (chỉ một) là một node đầu ra. Vì vậy, mạng của chúng tôi có 2 đầu vào và 1 đầu ra.

Dòng 20: Thực hành tốt để gieo số ngẫu nhiên của bạn. Số của bạn vẫn sẽ được phân phối ngẫu nhiên, nhưng chúng sẽ được phân phối ngẫu nhiên theo cách chính xác mỗi lần bạn đào tạo. Điều này làm cho việc xem các thay đổi của bạn ảnh hưởng đến mạng như thế nào dễ dàng hơn.

Dòng 23: Đây là ma trận trọng số của chúng ta cho Neuron Network này. Nó được gọi là "syn0" để ngụ ý "synapse zero". Vì chúng ta chỉ có 2 lớp (đầu vào và đầu ra), chúng ta chỉ cần một ma trận trọng lượng để kết nối chúng. Kích thước của nó là (2,1) bởi vì chúng ta có 2 đầu vào và 1 đầu ra. Một cách khác để nhìn vào nó là l0 có kích thước 2 và l1 có kích thước 1. Vì vậy, chúng ta muốn kết nối mọi nút trong l0 tới mỗi nút trong l1, đòi hỏi ma trận dimensionality (2,1). 😃

Cũng lưu ý rằng nó được khởi tạo ngẫu nhiên với một trung bình là số không. Có một chút lý thuyết mà đi vào khởi tạo trọng lượng. Bây giờ, hãy coi nó như là một phương pháp hay nhất, đó là một ý tưởng tốt để có một ý nghĩa của số không khởi tạo trọng lượng.

Một lưu ý khác là Neuron Network thực sự chỉ là ma trận này. Chúng ta có các lớp l0 và l1 nhưng chúng là các giá trị tạm thời dựa trên bộ dữ liệu. Chúng ta không lưu trữ nó. Tất cả các dữ liệu học được lưu trữ trong ma trận syn0.

Dòng 25: Điều này bắt đầu code của chúng tôi để training Neuron Network. Điều này cho vòng lặp "lặp đi lặp lại" nhiều lần qua mã đào tạo để tối ưu hóa mạng của chúng tôi với tập dữ liệu.

Dòng 28: Vì lớp đầu tiên của chúng ta, l0, đơn giản chỉ là dữ liệu của chúng ta. Chúng tôi mô tả rõ ràng nó như vậy tại thời điểm này. Nhớ rằng X chứa 4 ví dụ training (hàng). Chúng ta sẽ xử lý tất cả chúng trong cùng một thời gian trong quá trình thực hiện. Như vậy, chúng ta có 4 hàng khác nhau l0, nhưng bạn có thể nghĩ nó như là một ví dụ đào tạo đơn nếu bạn muốn. Nó không có sự khác biệt vào thời điểm này.

Dòng 29: Đây là bước tiên đoán của chúng tôi. Về cơ bản, trước tiên chúng ta để cho mạng "thử" để dự đoán đầu ra cho đầu vào. Sau đó chúng tôi sẽ nghiên cứu cách thức thực hiện để chúng tôi có thể điều chỉnh nó để làm tốt hơn một chút cho mỗi lần lặp.

Dòng này gồm 2 bước. Ma trận đầu tiên nhân l0 bởi syn0. Thứ hai đi đầu ra của chúng tôi thông qua các chức năng sigmoid. Hãy xem xét kích thước của mỗi loại:

(4 x 2) dot (2 x 1) = (4 x 1)

Phép nhân ma trận được sắp xếp, như kích thước ở giữa của phương trình phải giống nhau. Ma trận cuối cùng tạo ra là số hàng của ma trận đầu tiên và số cột của ma trận thứ hai.

Kể từ khi chúng tôi nạp trong 4 ví dụ training, chúng tôi đã kết thúc với 4 đoán cho câu trả lời đúng, ma trận (4 x 1). Mỗi đầu ra tương ứng với dự đoán của mạng cho một đầu vào nhất định. Có lẽ nó trở nên trực quan tại sao chúng ta có thể nạp vào một số tùy ý của các ví dụ training. Việc nhân rộng ma trận vẫn sẽ làm việc ra. 😃

Dòng 32: Vì vậy, cho rằng l1 có một dự đoán cho mỗi đầu vào. Bây giờ chúng ta có thể so sánh nó bằng cách nào bằng cách trừ câu trả lời đúng (y) khỏi kết quả dự đoán (l1). L1_error chỉ là một vector của các số dương và âm phản ánh sai số mà Network bị mất.

Dòng 36: Bây giờ chúng tôi đang nhận được những thứ tốt! Đây là nước sốt bí mật! Có rất nhiều điều đang xảy ra trong dòng này, vì vậy chúng ta hãy chia nó thành hai phần.

Phần 1: Derivative

nonlin(l1,True)

Nếu l1 đại diện cho ba chấm, mã trên tạo ra các đường dốc của các đường bên dưới. Lưu ý rằng các giá trị rất cao như x = 2,0 (điểm màu xanh lá cây) và các giá trị rất thấp như x = -1,0 (chấm màu tím) có độ dốc nông hơn. Độ dốc cao nhất bạn có thể có ở x = 0 (chấm màu xanh). Điều này đóng một vai trò quan trọng. Cũng lưu ý rằng tất cả các Derivative là từ 0 đến 1.

Phần 2: Error Weighted Derivative

l1_delta = l1_error * nonlin(l1,True)

Có nhiều cách toán học chính xác hơn Error Weighted Derivative nhưng tôi nghĩ rằng điều này nắm bắt trực giác. L1_error là ma trận (4,1). nonlin(l1, True) trả về ma trận (4,1). Những gì chúng ta đang làm là nhân chúng lên. Điều này trả về ma trận (4,1) l1_delta với giá trị nhân.

Khi chúng ta nhân "sườn dốc" do lỗi, chúng ta đang giảm thiểu các lỗi của dự đoán độ tin cậy cao. Nhìn vào hình ảnh sigmoid một lần nữa! Nếu độ dốc thực sự nông (gần với 0), thì mạng có giá trị rất cao hoặc giá trị rất thấp. Điều này có nghĩa là mạng đã được khá tự tin theo cách này hay cách khác. Tuy nhiên, nếu mạng đoán một cái gì đó gần (x = 0, y = 0.5) thì nó không phải là rất tự tin. Chúng tôi cập nhật những dự đoán "mơ màng" nhất, và chúng tôi có xu hướng để lại những người tự tin một mình bằng cách nhân chúng với một số gần 0.

Dòng 39: Chúng tôi đã sẵn sàng để cập nhật mạng của chúng tôi!

Lời nhắn

Nếu bạn rất muốn học về Neuron Network, tôi có một đề nghị. Hãy thử xây dựng lại Neuron Network này từ những gì bạn nhớ. Tôi biết rằng có thể nghe có gì đó điên rồ, nhưng nó nghiêm túc giúp bạn hiểu sâu về nó. Tôi đã làm việc với Neuron Network trong một vài năm trước khi thực hiện bài tập này, và đó là sự đầu tư tốt nhất của thời gian tôi đã thực hiện trong lĩnh vực này (và nó không mất nhiều thời gian).

Nguồn tham khảo

http://iamtrask.github.io/2015/07/12/basic-python-network/


All Rights Reserved