TensorFlow, Keras và mô hình học sâu mà không cần tiến sĩ

Trong lớp học lập trình này, bạn sẽ tìm hiểu cách xây dựng và huấn luyện một mạng nơ-ron có thể nhận dạng chữ số viết tay. Trong quá trình này, khi cải thiện mạng nơ-ron để đạt được độ chính xác 99%, bạn cũng sẽ khám phá ra những công cụ mà các chuyên gia học sâu sử dụng để huấn luyện mô hình một cách hiệu quả.

Lớp học lập trình này sử dụng tập dữ liệu MNIST, một tập hợp gồm 60.000 chữ số được gắn nhãn và đã khiến nhiều thế hệ tiến sĩ bận rộn trong gần 2 thập kỷ. Bạn sẽ giải quyết vấn đề này bằng chưa đến 100 dòng mã Python / TensorFlow.

Kiến thức bạn sẽ học được

  • Mạng nơ-ron là gì và cách huấn luyện mạng nơ-ron
  • Cách xây dựng một mạng nơ-ron cơ bản gồm 1 lớp bằng tf.keras
  • Cách thêm nhiều lớp
  • Cách thiết lập lịch trình tốc độ học
  • Cách xây dựng mạng nơ-ron tích chập
  • Cách sử dụng các kỹ thuật điều chuẩn: dropout, chuẩn hoá theo lô
  • Hiện tượng khớp quá mức là gì

Bạn cần có

Chỉ cần một trình duyệt. Bạn có thể thực hiện toàn bộ hội thảo này bằng Google Colaboratory.

Phản hồi

Vui lòng cho chúng tôi biết nếu bạn thấy có điều gì sai sót trong phòng thí nghiệm này hoặc nếu bạn nghĩ rằng chúng tôi nên cải thiện. Chúng tôi xử lý ý kiến phản hồi thông qua các vấn đề trên GitHub [đường liên kết phản hồi].

Phòng thí nghiệm này sử dụng Google Colaboratory và bạn không cần thiết lập gì. Bạn có thể chạy ứng dụng này trên Chromebook. Vui lòng mở tệp bên dưới và thực thi các ô để làm quen với sổ tay Colab.

Welcome to Colab.ipynb

Hướng dẫn bổ sung bên dưới:

Chọn một phần phụ trợ GPU

Trong trình đơn Colab, hãy chọn Thời gian chạy > Thay đổi loại thời gian chạy rồi chọn GPU. Quá trình kết nối với thời gian chạy sẽ diễn ra tự động trong lần thực thi đầu tiên hoặc bạn có thể sử dụng nút "Kết nối" ở góc trên bên phải.

Thực thi sổ tay

Thực thi từng ô bằng cách nhấp vào một ô và sử dụng tổ hợp phím Shift-ENTER. Bạn cũng có thể chạy toàn bộ sổ tay bằng cách chọn Thời gian chạy > Chạy tất cả

Mục lục

Tất cả sổ tay đều có mục lục. Bạn có thể mở bảng này bằng cách nhấn vào mũi tên màu đen ở bên trái.

Các ô bị ẩn

Một số ô sẽ chỉ hiển thị tiêu đề. Đây là một tính năng sổ tay dành riêng cho Colab. Bạn có thể nhấp đúp vào các tệp này để xem mã bên trong, nhưng thường thì mã này không có gì thú vị. Thường là các hàm hỗ trợ hoặc trực quan hoá. Bạn vẫn cần chạy các ô này để xác định các hàm bên trong.

Trước tiên, chúng ta sẽ xem một mạng nơ-ron huấn luyện. Vui lòng mở sổ tay bên dưới và chạy qua tất cả các ô. Hiện tại, bạn không cần chú ý đến đoạn mã này, chúng ta sẽ bắt đầu giải thích đoạn mã này sau.

keras_01_mnist.ipynb

Khi bạn thực thi sổ tay, hãy tập trung vào các hình ảnh trực quan. Hãy xem phần giải thích bên dưới.

Dữ liệu huấn luyện

Chúng ta có một tập dữ liệu gồm các chữ số viết tay đã được gắn nhãn để biết mỗi bức ảnh biểu thị điều gì, tức là một số từ 0 đến 9. Trong sổ tay, bạn sẽ thấy một đoạn trích:

Mạng nơ-ron mà chúng ta sẽ xây dựng phân loại các chữ số viết tay trong 10 lớp (0, ..., 9). Việc này được thực hiện dựa trên các tham số nội bộ cần có giá trị chính xác để hoạt động phân loại diễn ra hiệu quả. "Giá trị chính xác" này được học thông qua một quy trình huấn luyện đòi hỏi phải có "tập dữ liệu được gắn nhãn" có hình ảnh và câu trả lời chính xác liên quan.

Làm cách nào để biết mạng nơ-ron đã được huấn luyện có hoạt động hiệu quả hay không? Việc sử dụng tập dữ liệu huấn luyện để kiểm thử mạng sẽ là hành vi gian lận. Mô hình đã thấy tập dữ liệu đó nhiều lần trong quá trình huấn luyện và chắc chắn sẽ hoạt động rất hiệu quả trên tập dữ liệu đó. Chúng ta cần một tập dữ liệu được gắn nhãn khác, chưa từng thấy trong quá trình huấn luyện, để đánh giá hiệu suất "thế giới thực" của mạng. Đó là "tập dữ liệu xác thực"

Đào tạo

Khi quá trình huấn luyện diễn ra, mỗi lần một lô dữ liệu huấn luyện, các thông số mô hình nội bộ sẽ được cập nhật và mô hình sẽ ngày càng nhận dạng tốt hơn các chữ số viết tay. Bạn có thể thấy điều này trên biểu đồ huấn luyện:

Ở bên phải, "độ chính xác" chỉ là tỷ lệ phần trăm số chữ số được nhận dạng chính xác. Chỉ số này sẽ tăng lên khi quá trình huấn luyện tiến triển, điều này là tốt.

Ở bên trái, chúng ta có thể thấy "mất mát". Để thúc đẩy quá trình huấn luyện, chúng ta sẽ xác định một hàm "mất mát" (loss function), cho biết mức độ hệ thống nhận dạng các chữ số kém đến mức nào và cố gắng giảm thiểu hàm này. Điều bạn thấy ở đây là mức tổn thất giảm xuống trên cả dữ liệu huấn luyện và dữ liệu xác thực khi quá trình huấn luyện diễn ra: đó là điều tốt. Điều này có nghĩa là mạng nơron đang học.

Trục X biểu thị số lượng "epoch" hoặc số lần lặp lại thông qua toàn bộ tập dữ liệu.

Thông tin dự đoán

Khi mô hình được huấn luyện, chúng ta có thể sử dụng mô hình đó để nhận dạng các chữ số viết tay. Hình ảnh trực quan tiếp theo cho thấy hiệu suất của ứng dụng trên một vài chữ số được kết xuất từ phông chữ cục bộ (dòng đầu tiên), sau đó là trên 10.000 chữ số của tập dữ liệu xác thực. Lớp được dự đoán xuất hiện bên dưới mỗi chữ số, có màu đỏ nếu đó là lớp không chính xác.

Như bạn thấy, mô hình ban đầu này không tốt lắm nhưng vẫn nhận dạng chính xác một số chữ số. Độ chính xác xác thực cuối cùng của mô hình này là khoảng 90%. Đây không phải là một con số quá tệ đối với mô hình đơn giản mà chúng ta đang bắt đầu, nhưng điều này vẫn có nghĩa là mô hình này bỏ lỡ 1.000 chữ số xác thực trong số 10.000 chữ số. Số lượng câu trả lời đó nhiều hơn nhiều so với số lượng có thể hiển thị, đó là lý do tại sao có vẻ như tất cả câu trả lời đều sai (màu đỏ).

Tensor

Dữ liệu được lưu trữ trong ma trận. Hình ảnh thang độ xám 28x28 pixel phù hợp với ma trận hai chiều 28x28. Nhưng đối với hình ảnh màu, chúng ta cần nhiều chiều hơn. Có 3 giá trị màu cho mỗi pixel (Đỏ, Lục, Lam), vì vậy, bạn sẽ cần một bảng ba chiều có kích thước [28, 28, 3]. Để lưu trữ một lô gồm 128 hình ảnh màu, bạn cần một bảng 4 chiều có kích thước [128, 28, 28, 3].

Các bảng đa chiều này được gọi là "tensor" và danh sách các chiều của chúng là "hình dạng".

Tóm tắt

Nếu đã biết tất cả các thuật ngữ được in đậm trong đoạn văn tiếp theo, bạn có thể chuyển sang bài tập tiếp theo. Nếu bạn chỉ mới bắt đầu tìm hiểu về học sâu, thì xin chào mừng bạn và vui lòng đọc tiếp.

witch.png

Đối với các mô hình được tạo dưới dạng một chuỗi các lớp, Keras cung cấp API tuần tự. Ví dụ: bạn có thể viết một trình phân loại hình ảnh bằng cách sử dụng 3 lớp dày đặc trong Keras như sau:

model = tf.keras.Sequential([
    tf.keras.layers.Flatten(input_shape=[28, 28, 1]),
    tf.keras.layers.Dense(200, activation="relu"),
    tf.keras.layers.Dense(60, activation="relu"),
    tf.keras.layers.Dense(10, activation='softmax') # classifying into 10 classes
])

# this configures the training of the model. Keras calls it "compiling" the model.
model.compile(
  optimizer='adam',
  loss= 'categorical_crossentropy',
  metrics=['accuracy']) # % of correct answers

# train the model
model.fit(dataset, ... )

Một lớp dày đặc

Các chữ số viết tay trong tập dữ liệu MNIST là hình ảnh thang độ xám có kích thước 28x28 pixel. Cách đơn giản nhất để phân loại các chữ số này là sử dụng 28x28=784 pixel làm dữ liệu đầu vào cho một mạng nơ-ron 1 lớp.

Screen Shot 2016-07-26 at 12.32.24.png

Mỗi "nơ-ron" trong mạng nơ-ron sẽ tính tổng có trọng số của tất cả các đầu vào, thêm một hằng số gọi là "độ lệch" rồi truyền kết quả thông qua một số "hàm kích hoạt" phi tuyến tính. "trọng số" "độ lệch" là các tham số sẽ được xác định thông qua quá trình huấn luyện. Ban đầu, chúng được khởi tạo bằng các giá trị ngẫu nhiên.

Hình ảnh trên biểu thị một mạng nơ-ron 1 lớp với 10 nơ-ron đầu ra vì chúng ta muốn phân loại các chữ số thành 10 lớp (0 đến 9).

Với phép nhân ma trận

Sau đây là cách một lớp mạng nơ-ron xử lý một tập hợp hình ảnh có thể được biểu thị bằng phép nhân ma trận:

matmul.gif

Sử dụng cột trọng số đầu tiên trong ma trận trọng số W, chúng ta tính tổng trọng số của tất cả các pixel trong hình ảnh đầu tiên. Tổng này tương ứng với nơ-ron đầu tiên. Sử dụng cột trọng số thứ hai, chúng ta làm tương tự cho nơ-ron thứ hai, v.v. cho đến nơ-ron thứ 10. Sau đó, chúng ta có thể lặp lại thao tác này cho 99 hình ảnh còn lại. Nếu chúng ta gọi X là ma trận chứa 100 hình ảnh, thì tất cả các tổng có trọng số cho 10 nơ-ron, được tính trên 100 hình ảnh chỉ đơn giản là X.W, một phép nhân ma trận.

Giờ đây, mỗi nơ-ron phải thêm độ lệch (một hằng số). Vì có 10 nơ-ron nên chúng ta có 10 hằng số thiên vị. Chúng ta sẽ gọi vectơ gồm 10 giá trị này là b. Bạn phải thêm giá trị này vào từng dòng của ma trận đã tính toán trước đó. Bằng cách sử dụng một chút "phép thuật" gọi là "truyền tin", chúng ta sẽ viết điều này bằng một dấu cộng đơn giản.

Cuối cùng, chúng ta áp dụng một hàm kích hoạt, chẳng hạn như "softmax" (được giải thích bên dưới) và nhận được công thức mô tả một mạng nơ-ron 1 lớp, được áp dụng cho 100 hình ảnh:

Screen Shot 2016-07-26 at 16.02.36.png

Trong Keras

Với các thư viện mạng nơ-ron cấp cao như Keras, chúng ta sẽ không cần triển khai công thức này. Tuy nhiên, điều quan trọng là bạn phải hiểu rằng lớp mạng nơ-ron chỉ là một loạt các phép nhân và phép cộng. Trong Keras, một lớp dày đặc sẽ được viết như sau:

tf.keras.layers.Dense(10, activation='softmax')

Tìm hiểu sâu

Việc liên kết các lớp mạng nơ-ron là điều không khó. Lớp đầu tiên tính tổng có trọng số của các pixel. Các lớp tiếp theo sẽ tính tổng có trọng số của đầu ra của các lớp trước đó.

Điểm khác biệt duy nhất, ngoài số lượng nơ-ron, sẽ là lựa chọn về hàm kích hoạt.

Hàm kích hoạt: relu, softmax và sigmoid

Thông thường, bạn sẽ sử dụng hàm kích hoạt "relu" cho tất cả các lớp, trừ lớp cuối cùng. Lớp cuối cùng trong một trình phân loại sẽ sử dụng cơ chế kích hoạt "softmax".

Tương tự, một "nơ-ron" tính tổng có trọng số của tất cả các đầu vào, thêm một giá trị gọi là "độ lệch" và truyền kết quả thông qua hàm kích hoạt.

Hàm kích hoạt phổ biến nhất được gọi là "RELU" cho Rectified Linear Unit (Đơn vị tuyến tính được chỉnh sửa). Đây là một hàm rất đơn giản như bạn có thể thấy trên biểu đồ ở trên.

Hàm kích hoạt truyền thống trong mạng nơ-ron là "sigmoid" nhưng "relu" đã cho thấy các thuộc tính hội tụ tốt hơn ở hầu hết mọi nơi và hiện được ưu tiên.

Hàm kích hoạt Softmax để phân loại

Lớp cuối cùng của mạng nơ-ron có 10 nơ-ron vì chúng ta muốn phân loại chữ số viết tay thành 10 lớp (0 đến 9). Nó sẽ xuất ra 10 số từ 0 đến 1, biểu thị xác suất của chữ số này là 0, 1, 2, v.v. Đối với việc này, trên lớp cuối cùng, chúng ta sẽ sử dụng một hàm kích hoạt có tên là "softmax".

Việc áp dụng softmax trên một vectơ được thực hiện bằng cách lấy hàm mũ của từng phần tử rồi chuẩn hoá vectơ, thường là bằng cách chia vectơ đó cho chuẩn "L1" (tức là tổng các giá trị tuyệt đối) để các giá trị được chuẩn hoá cộng lại thành 1 và có thể được diễn giải là xác suất.

Đầu ra của lớp cuối cùng, trước khi kích hoạt đôi khi được gọi là "logits". Nếu vectơ này là L = [L0, L1, L2, L3, L4, L5, L6, L7, L8, L9], thì:

Hàm mất mát cross-entropy

Giờ đây, khi mạng nơ-ron của chúng ta tạo ra các dự đoán từ hình ảnh đầu vào, chúng ta cần đo lường mức độ chính xác của các dự đoán đó, tức là khoảng cách giữa những gì mạng nơ-ron cho chúng ta biết và câu trả lời chính xác, thường được gọi là "nhãn". Hãy nhớ rằng chúng tôi có nhãn chính xác cho tất cả hình ảnh trong tập dữ liệu.

Bất kỳ khoảng cách nào cũng có thể dùng được, nhưng đối với các vấn đề về phân loại, "khoảng cách entropy chéo" là hiệu quả nhất. Chúng ta sẽ gọi đây là hàm lỗi hoặc "mất mát":

Phương pháp giảm độ dốc

"Huấn luyện" mạng nơ-ron thực sự có nghĩa là sử dụng hình ảnh và nhãn huấn luyện để điều chỉnh trọng số và độ chệch nhằm giảm thiểu hàm mất mát entropy chéo. Sau đây là cách hoạt động của tính năng này.

Cross-entropy là một hàm của trọng số, độ lệch, pixel của hình ảnh huấn luyện và lớp đã biết của hình ảnh đó.

Nếu tính đạo hàm riêng của hàm mất mát tương đối với tất cả các trọng số và tất cả các độ lệch, chúng ta sẽ thu được một "độ dốc", được tính cho một hình ảnh, nhãn và giá trị hiện tại của trọng số và độ lệch nhất định. Hãy nhớ rằng chúng ta có thể có hàng triệu trọng số và độ lệch, vì vậy, việc tính toán độ dốc có vẻ như là một việc tốn nhiều công sức. Rất may là TensorFlow sẽ thực hiện việc này cho chúng ta. Thuộc tính toán học của một độ dốc là nó hướng "lên". Vì chúng ta muốn đi đến nơi có entropy chéo thấp, nên chúng ta sẽ đi theo hướng ngược lại. Chúng ta cập nhật các trọng số và độ lệch theo một phần của độ dốc. Sau đó, chúng ta sẽ lặp lại quy trình này nhiều lần bằng cách sử dụng các lô hình ảnh và nhãn huấn luyện tiếp theo trong một vòng lặp huấn luyện. Hy vọng rằng điều này sẽ hội tụ đến một nơi có entropy chéo tối thiểu, mặc dù không có gì đảm bảo rằng mức tối thiểu này là duy nhất.

gradient descent2.png

Mini-batching và động lượng

Bạn có thể tính toán độ dốc chỉ trên một hình ảnh mẫu và cập nhật ngay các trọng số và độ lệch, nhưng khi thực hiện trên một lô gồm 128 hình ảnh (ví dụ), bạn sẽ nhận được độ dốc thể hiện tốt hơn các ràng buộc do nhiều hình ảnh mẫu áp đặt và do đó có khả năng hội tụ về giải pháp nhanh hơn. Kích thước của lô nhỏ là một tham số có thể điều chỉnh.

Kỹ thuật này, đôi khi được gọi là "hạ dốc ngẫu nhiên", có một lợi ích thực tế hơn: làm việc với các lô cũng có nghĩa là làm việc với các ma trận lớn hơn và những ma trận này thường dễ dàng tối ưu hoá trên GPU và TPU.

Tuy nhiên, quá trình hội tụ vẫn có thể hơi hỗn loạn và thậm chí có thể dừng lại nếu vectơ độ dốc đều bằng 0. Điều đó có nghĩa là chúng ta đã tìm thấy một giá trị tối thiểu? Không phải lúc nào cũng vậy. Thành phần chuyển màu có thể bằng 0 ở mức tối thiểu hoặc tối đa. Với một vectơ gradient có hàng triệu phần tử, nếu tất cả đều bằng 0, thì xác suất để mọi số 0 tương ứng với một điểm tối thiểu và không có số 0 nào tương ứng với một điểm tối đa là khá nhỏ. Trong không gian nhiều chiều, điểm yên ngựa khá phổ biến và chúng ta không muốn dừng lại ở đó.

Hình minh hoạ: điểm yên ngựa. Độ dốc bằng 0 nhưng không phải là độ dốc tối thiểu theo mọi hướng. (Thông tin ghi nhận tác giả của hình ảnh Wikimedia: Tác giả Nicoguaro – Tự sáng tạo, CC BY 3.0)

Giải pháp là thêm một số động lực cho thuật toán tối ưu hoá để thuật toán có thể vượt qua các điểm yên mà không dừng lại.

Bảng thuật ngữ

batch hoặc mini-batch: quá trình huấn luyện luôn được thực hiện trên các lô dữ liệu huấn luyện và nhãn. Việc này giúp thuật toán hội tụ. Phương diện "lô" thường là phương diện đầu tiên của các tensor dữ liệu. Ví dụ: một tensor có hình dạng [100, 192, 192, 3] chứa 100 hình ảnh có kích thước 192x192 pixel với 3 giá trị trên mỗi pixel (RGB).

mất entropy chéo: một hàm mất mát đặc biệt thường được dùng trong các trình phân loại.

lớp dày đặc: một lớp nơ-ron, trong đó mỗi nơ-ron được kết nối với tất cả các nơ-ron trong lớp trước đó.

đặc điểm: đầu vào của một mạng nơ-ron đôi khi được gọi là "đặc điểm". Nghệ thuật tìm ra những phần nào của một tập dữ liệu (hoặc tổ hợp các phần) cần đưa vào mạng nơ-ron để có được dự đoán chính xác được gọi là "kỹ thuật đặc trưng".

nhãn: một tên gọi khác của "lớp" hoặc câu trả lời đúng trong vấn đề phân loại có giám sát

tốc độ học tập: phần nhỏ của độ dốc mà theo đó các trọng số và độ lệch được cập nhật ở mỗi lần lặp lại của vòng lặp huấn luyện.

logits: đầu ra của một lớp nơ-ron trước khi hàm kích hoạt được áp dụng được gọi là "logits". Thuật ngữ này bắt nguồn từ "hàm logistic", còn gọi là "hàm sigmoid", từng là hàm kích hoạt phổ biến nhất. "Đầu ra của nơ-ron trước hàm logistic" được rút ngắn thành "logits".

loss: hàm lỗi so sánh đầu ra của mạng nơ-ron với câu trả lời chính xác

neuron: tính tổng có trọng số của các đầu vào, thêm một độ lệch và truyền kết quả thông qua một hàm kích hoạt.

mã hoá one-hot: lớp 3 trong số 5 được mã hoá dưới dạng một vectơ gồm 5 phần tử, tất cả đều là 0 ngoại trừ phần tử thứ 3 là 1.

relu: đơn vị tuyến tính được chỉnh sửa. Một hàm kích hoạt phổ biến cho các nơ-ron.

sigmoid: một hàm kích hoạt khác từng phổ biến và vẫn hữu ích trong các trường hợp đặc biệt.

softmax: một hàm kích hoạt đặc biệt hoạt động trên một vectơ, làm tăng sự khác biệt giữa thành phần lớn nhất và tất cả các thành phần khác, đồng thời chuẩn hoá vectơ để có tổng bằng 1, nhờ đó có thể diễn giải vectơ này dưới dạng vectơ xác suất. Được dùng làm bước cuối cùng trong các trình phân loại.

tensor: "tensor" giống như ma trận nhưng có số lượng chiều tuỳ ý. Tensor 1 chiều là một vectơ. Tensor 2 chiều là một ma trận. Sau đó, bạn có thể có các tensor với 3, 4, 5 hoặc nhiều chiều hơn.

Quay lại sổ tay nghiên cứu và lần này, hãy đọc mã.

keras_01_mnist.ipynb

Hãy cùng xem qua tất cả các ô trong sổ tay này.

Ô "Tham số"

Kích thước lô, số lượng giai đoạn huấn luyện và vị trí của các tệp dữ liệu được xác định ở đây. Các tệp dữ liệu được lưu trữ trong một bộ chứa Google Cloud Storage (GCS), đó là lý do địa chỉ của các tệp này bắt đầu bằng gs://

Ô "Nhập"

Tất cả các thư viện Python cần thiết đều được nhập tại đây, bao gồm cả TensorFlow và matplotlib để trực quan hoá.

Ô "visualization utilities [RUN ME]"

Ô này chứa mã trực quan hoá không thú vị. Theo mặc định, tệp này sẽ được thu gọn nhưng bạn có thể mở và xem mã khi có thời gian bằng cách nhấp đúp vào tệp đó.

Ô "tf.data.Dataset: parse files and prepare training and validation datasets"

Ô này đã sử dụng API tf.data.Dataset để tải tập dữ liệu MNIST từ các tệp dữ liệu. Bạn không cần dành quá nhiều thời gian cho ô này. Nếu bạn quan tâm đến API tf.data.Dataset, hãy xem hướng dẫn giải thích về API này: Các quy trình dữ liệu có tốc độ TPU. Hiện tại, những điều cơ bản là:

Hình ảnh và nhãn (câu trả lời đúng) từ tập dữ liệu MNIST được lưu trữ trong các bản ghi có độ dài cố định trong 4 tệp. Bạn có thể tải các tệp này bằng hàm bản ghi cố định chuyên dụng:

imagedataset = tf.data.FixedLengthRecordDataset(image_filename, 28*28, header_bytes=16)

Giờ đây, chúng ta có một tập dữ liệu gồm các byte hình ảnh. Chúng cần được giải mã thành hình ảnh. Chúng ta xác định một hàm để thực hiện việc này. Hình ảnh không được nén nên hàm không cần giải mã bất cứ thứ gì (về cơ bản, decode_raw không làm gì cả). Sau đó, hình ảnh được chuyển đổi thành các giá trị dấu phẩy động từ 0 đến 1. Chúng ta có thể định hình lại nó ở đây dưới dạng hình ảnh 2D nhưng thực tế chúng ta giữ nó dưới dạng một mảng phẳng gồm các pixel có kích thước 28*28 vì đó là những gì lớp dày đặc ban đầu của chúng ta mong đợi.

def read_image(tf_bytestring):
    image = tf.decode_raw(tf_bytestring, tf.uint8)
    image = tf.cast(image, tf.float32)/256.0
    image = tf.reshape(image, [28*28])
    return image

Chúng ta áp dụng hàm này cho tập dữ liệu bằng cách sử dụng .map và thu được một tập dữ liệu gồm các hình ảnh:

imagedataset = imagedataset.map(read_image, num_parallel_calls=16)

Chúng ta cũng đọc và giải mã nhãn theo cách tương tự, đồng thời .zip hình ảnh và nhãn với nhau:

dataset = tf.data.Dataset.zip((imagedataset, labelsdataset))

Giờ đây, chúng ta có một tập dữ liệu gồm các cặp (hình ảnh, nhãn). Đây là điều mà mô hình của chúng tôi mong đợi. Chúng ta chưa sẵn sàng sử dụng nó trong hàm huấn luyện:

dataset = dataset.cache()
dataset = dataset.shuffle(5000, reshuffle_each_iteration=True)
dataset = dataset.repeat()
dataset = dataset.batch(batch_size)
dataset = dataset.prefetch(tf.data.experimental.AUTOTUNE)

API tf.data.Dataset có tất cả hàm tiện ích cần thiết để chuẩn bị tập dữ liệu:

.cache lưu tập dữ liệu vào bộ nhớ truy cập ngẫu nhiên (RAM). Đây là một tập dữ liệu nhỏ nên sẽ hoạt động. .shuffle xáo trộn dữ liệu này bằng một vùng đệm gồm 5.000 phần tử. Điều quan trọng là dữ liệu huấn luyện phải được xáo trộn kỹ lưỡng. .repeat lặp lại tập dữ liệu. Chúng ta sẽ huấn luyện trên đó nhiều lần (nhiều giai đoạn). .batch sẽ kết hợp nhiều hình ảnh và nhãn thành một đoạn video ngắn. Cuối cùng, .prefetch có thể sử dụng CPU để chuẩn bị lô tiếp theo trong khi lô hiện tại đang được huấn luyện trên GPU.

Tập dữ liệu xác thực được chuẩn bị theo cách tương tự. Giờ đây, chúng ta đã sẵn sàng xác định một mô hình và sử dụng tập dữ liệu này để huấn luyện mô hình đó.

Ô "Keras Model" (Mô hình Keras)

Tất cả các mô hình của chúng ta sẽ là các chuỗi lớp thẳng để chúng ta có thể sử dụng kiểu tf.keras.Sequential để tạo các mô hình đó. Ban đầu, đây là một lớp dày đặc duy nhất. Mạng này có 10 nơ-ron vì chúng ta đang phân loại chữ số viết tay thành 10 lớp. Nó sử dụng cơ chế kích hoạt "softmax" vì đây là lớp cuối cùng trong một trình phân loại.

Mô hình Keras cũng cần biết hình dạng của dữ liệu đầu vào. Bạn có thể dùng tf.keras.layers.Input để xác định. Ở đây, các vectơ đầu vào là vectơ phẳng có giá trị pixel có độ dài 28*28.

model = tf.keras.Sequential(
  [
    tf.keras.layers.Input(shape=(28*28,)),
    tf.keras.layers.Dense(10, activation='softmax')
  ])

model.compile(optimizer='sgd',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# print model layers
model.summary()

# utility callback that displays training curves
plot_training = PlotTraining(sample_rate=10, zoom=1)

Bạn có thể định cấu hình mô hình trong Keras bằng hàm model.compile. Ở đây, chúng ta sử dụng trình tối ưu hoá cơ bản 'sgd' (Stochastic Gradient Descent). Mô hình phân loại yêu cầu hàm tổn thất entropy chéo, được gọi là 'categorical_crossentropy' trong Keras. Cuối cùng, chúng ta yêu cầu mô hình tính toán chỉ số 'accuracy', là tỷ lệ phần trăm hình ảnh được phân loại chính xác.

Keras cung cấp tiện ích model.summary() rất hữu ích để in thông tin chi tiết về mô hình mà bạn đã tạo. Người hướng dẫn chu đáo của bạn đã thêm tiện ích PlotTraining (được xác định trong ô "tiện ích trực quan hoá") để hiển thị nhiều đường cong huấn luyện trong quá trình huấn luyện.

Ô "Huấn luyện và xác thực mô hình"

Đây là nơi quá trình huấn luyện diễn ra, bằng cách gọi model.fit và truyền cả tập dữ liệu huấn luyện và xác thực. Theo mặc định, Keras chạy một vòng xác thực ở cuối mỗi giai đoạn.

model.fit(training_dataset, steps_per_epoch=steps_per_epoch, epochs=EPOCHS,
          validation_data=validation_dataset, validation_steps=1,
          callbacks=[plot_training])

Trong Keras, bạn có thể thêm các hành vi tuỳ chỉnh trong quá trình huấn luyện bằng cách sử dụng lệnh gọi lại. Đó là cách triển khai biểu đồ huấn luyện cập nhật linh hoạt cho hội thảo này.

Ô "Trực quan hoá dữ liệu dự đoán"

Sau khi huấn luyện mô hình, chúng ta có thể nhận được các dự đoán từ mô hình bằng cách gọi model.predict():

probabilities = model.predict(font_digits, steps=1)
predicted_labels = np.argmax(probabilities, axis=1)

Ở đây, chúng tôi đã chuẩn bị một bộ chữ số in được kết xuất từ các phông chữ cục bộ để kiểm thử. Hãy nhớ rằng mạng nơ-ron trả về một vectơ gồm 10 xác suất từ "softmax" cuối cùng. Để nhận được nhãn, chúng ta phải tìm ra xác suất cao nhất. np.argmax trong thư viện numpy sẽ thực hiện việc này.

Để hiểu lý do cần có tham số axis=1, vui lòng nhớ rằng chúng ta đã xử lý một lô gồm 128 hình ảnh và do đó, mô hình sẽ trả về 128 vectơ xác suất. Hình dạng của tensor đầu ra là [128, 10]. Chúng ta đang tính argmax trên 10 xác suất được trả về cho mỗi hình ảnh, do đó axis=1 (trục đầu tiên là 0).

Mô hình đơn giản này đã nhận dạng được 90% chữ số. Không tệ, nhưng giờ bạn sẽ cải thiện đáng kể điều này.

godeep.png

Để cải thiện độ chính xác của tính năng nhận dạng, chúng tôi sẽ thêm nhiều lớp hơn vào mạng nơ-ron.

Screen Shot 2016-07-27 at 15.36.55.png

Chúng ta giữ softmax làm hàm kích hoạt trên lớp cuối cùng vì đó là hàm phù hợp nhất cho việc phân loại. Tuy nhiên, trên các lớp trung gian, chúng ta sẽ sử dụng hàm kích hoạt cổ điển nhất: sigmoid:

Ví dụ: mô hình của bạn có thể trông như sau (đừng quên dấu phẩy, tf.keras.Sequential lấy danh sách các lớp được phân tách bằng dấu phẩy):

model = tf.keras.Sequential(
  [
      tf.keras.layers.Input(shape=(28*28,)),
      tf.keras.layers.Dense(200, activation='sigmoid'),
      tf.keras.layers.Dense(60, activation='sigmoid'),
      tf.keras.layers.Dense(10, activation='softmax')
  ])

Xem "thông tin tóm tắt" về mô hình của bạn. Giờ đây, chỉ số này có ít nhất gấp 10 lần số lượng tham số. Nó sẽ tốt hơn gấp 10 lần! Nhưng vì lý do nào đó, việc này không diễn ra ...

Khoản lỗ cũng có vẻ như đã tăng lên rất nhiều. Đã xảy ra sự cố.

Bạn vừa trải nghiệm mạng nơ-ron, như cách mọi người từng thiết kế chúng vào những năm 80 và 90. Không lạ gì khi họ từ bỏ ý tưởng này, dẫn đến cái gọi là "mùa đông AI". Thật vậy, khi bạn thêm các lớp, mạng nơ-ron sẽ ngày càng khó hội tụ.

Hoá ra, các mạng nơ-ron sâu có nhiều lớp (20, 50, thậm chí 100 lớp hiện nay) có thể hoạt động rất hiệu quả, miễn là có một vài thủ thuật toán học để giúp chúng hội tụ. Việc phát hiện ra những thủ thuật đơn giản này là một trong những lý do khiến học sâu hồi sinh vào những năm 2010.

Kích hoạt RELU

relu.png

Hàm kích hoạt sigmoid thực sự khá có vấn đề trong các mạng lưới sâu. Nó nén tất cả các giá trị trong khoảng từ 0 đến 1 và khi bạn thực hiện việc này nhiều lần, các đầu ra của nơ-ron và độ dốc của chúng có thể biến mất hoàn toàn. Điều này được đề cập vì lý do lịch sử, nhưng các mạng hiện đại sử dụng RELU (Đơn vị tuyến tính được chỉnh sửa) có dạng như sau:

Mặt khác, relu có đạo hàm bằng 1, ít nhất là ở phía bên phải. Với cơ chế kích hoạt RELU, ngay cả khi độ dốc đến từ một số nơ-ron có thể bằng 0, thì vẫn luôn có những nơ-ron khác cho độ dốc khác 0 rõ ràng và quá trình huấn luyện có thể tiếp tục với tốc độ tốt.

Công cụ tối ưu hoá hiệu quả hơn

Trong các không gian có chiều rất cao như ở đây (chúng ta có khoảng 10.000 trọng số và độ lệch), "điểm yên ngựa" xuất hiện thường xuyên. Đây là những điểm không phải là cực tiểu cục bộ, nhưng độ dốc vẫn bằng 0 và trình tối ưu hoá hạ độ dốc vẫn bị mắc kẹt ở đó. TensorFlow có đầy đủ các trình tối ưu hoá hiện có, bao gồm cả một số trình tối ưu hoá hoạt động với một lượng quán tính và sẽ vượt qua các điểm yên một cách an toàn.

Khởi tạo ngẫu nhiên

Nghệ thuật khởi tạo các trọng số thiên vị trước khi huấn luyện là một lĩnh vực nghiên cứu riêng, với nhiều bài báo được xuất bản về chủ đề này. Bạn có thể xem tất cả các trình khởi tạo có trong Keras tại đây. Rất may, Keras làm đúng theo mặc định và sử dụng trình khởi tạo 'glorot_uniform', đây là trình khởi tạo tốt nhất trong hầu hết mọi trường hợp.

Bạn không cần làm gì cả vì Keras đã thực hiện đúng thao tác.

NaN ???

Công thức entropy chéo liên quan đến một hàm lôgarit và log(0) không phải là một số (NaN, một sự cố về số nếu bạn muốn). Đầu vào của cross-entropy có thể là 0 không? Đầu vào đến từ softmax, về cơ bản là một hàm mũ và hàm mũ không bao giờ bằng 0. Vậy là chúng ta đã an toàn!

Thật sao? Trong thế giới toán học tươi đẹp, chúng ta sẽ an toàn, nhưng trong thế giới máy tính, exp(-150) được biểu thị ở định dạng float32 là ZERO và độ đo cross-entropy sẽ bị lỗi.

Rất may là bạn cũng không cần làm gì ở đây, vì Keras sẽ xử lý việc này và tính toán softmax, sau đó là entropy chéo theo cách đặc biệt cẩn thận để đảm bảo tính ổn định về số và tránh các NaN đáng sợ.

Thành công?

Giờ đây, bạn sẽ đạt được độ chính xác 97%. Mục tiêu của hội thảo này là đạt được tỷ lệ trên 99% một cách đáng kể, vì vậy, hãy tiếp tục cố gắng.

Nếu bạn gặp khó khăn, sau đây là giải pháp tại thời điểm này:

keras_02_mnist_dense.ipynb

Có lẽ chúng ta có thể cố gắng huấn luyện nhanh hơn? Tốc độ học tập mặc định trong trình tối ưu hoá Adam là 0,001. Hãy thử tăng số lượng này.

Có vẻ như việc tăng tốc không giúp ích nhiều và tiếng ồn này là gì vậy?

Các đường cong huấn luyện có nhiều biến động và hãy xem cả hai đường cong xác thực: chúng đang tăng và giảm liên tục. Điều này có nghĩa là chúng ta đang đi quá nhanh. Chúng ta có thể quay lại tốc độ trước đây, nhưng có một cách hiệu quả hơn.

slow down.png

Giải pháp tốt là bắt đầu nhanh và giảm tốc độ học theo cấp số nhân. Trong Keras, bạn có thể thực hiện việc này bằng lệnh gọi lại tf.keras.callbacks.LearningRateScheduler.

Mã hữu ích để sao chép và dán:

# lr decay function
def lr_decay(epoch):
  return 0.01 * math.pow(0.6, epoch)

# lr schedule callback
lr_decay_callback = tf.keras.callbacks.LearningRateScheduler(lr_decay, verbose=True)

# important to see what you are doing
plot_learning_rate(lr_decay, EPOCHS)

Đừng quên sử dụng lr_decay_callback mà bạn đã tạo. Thêm nó vào danh sách các lệnh gọi lại trong model.fit:

model.fit(...,  callbacks=[plot_training, lr_decay_callback])

Tác động của thay đổi nhỏ này là rất lớn. Bạn thấy rằng hầu hết nhiễu đã biến mất và độ chính xác của kiểm thử hiện ở mức trên 98% một cách bền vững.

Hiện tại, mô hình có vẻ đang hội tụ một cách hiệu quả. Hãy thử tìm hiểu sâu hơn nữa.

Thông tin này có giúp ích cho bạn không?

Không hẳn, độ chính xác vẫn ở mức 98% và hãy xem mức tổn thất xác thực. Số lượt xem đang tăng lên! Thuật toán học chỉ hoạt động trên dữ liệu huấn luyện và tối ưu hoá tổn thất huấn luyện cho phù hợp. Nó không bao giờ thấy dữ liệu xác thực, vì vậy, không có gì ngạc nhiên khi sau một thời gian, công việc của nó không còn ảnh hưởng đến tổn thất xác thực nữa. Tổn thất này ngừng giảm và đôi khi thậm chí còn tăng trở lại.

Điều này không ảnh hưởng ngay đến khả năng nhận dạng thực tế của mô hình, nhưng sẽ ngăn bạn chạy nhiều lần lặp lại và thường là dấu hiệu cho thấy quá trình huấn luyện không còn mang lại hiệu quả tích cực.

dropout.png

Sự khác biệt này thường được gọi là "quá khớp" và khi thấy sự khác biệt này, bạn có thể thử áp dụng một kỹ thuật điều chỉnh được gọi là "loại bỏ". Kỹ thuật bỏ qua sẽ loại bỏ các nơ-ron ngẫu nhiên ở mỗi lần lặp lại quá trình huấn luyện.

Nó có làm việc không?

Nhiễu xuất hiện trở lại (không có gì đáng ngạc nhiên khi xét đến cách hoạt động của tính năng loại bỏ). Mất mát xác thực dường như không tăng lên nữa, nhưng nhìn chung cao hơn so với khi không có dropout. Và độ chính xác của quá trình xác thực giảm đi một chút. Đây là một kết quả khá thất vọng.

Có vẻ như dropout không phải là giải pháp phù hợp, hoặc có thể "overfitting" là một khái niệm phức tạp hơn và một số nguyên nhân của nó không thể khắc phục bằng "dropout"?

"Quá khớp" là gì? Hiện tượng khớp quá mức xảy ra khi một mạng nơ-ron học "không tốt", theo cách hoạt động cho các ví dụ huấn luyện nhưng không hiệu quả với dữ liệu trong thế giới thực. Có những kỹ thuật điều chỉnh như dropout có thể buộc mô hình học theo cách tốt hơn, nhưng việc khớp quá mức cũng có những nguyên nhân sâu xa hơn.

overfitting.png

Tình trạng khớp quá mức cơ bản xảy ra khi mạng nơ-ron có quá nhiều mức độ tự do cho vấn đề hiện tại. Hãy tưởng tượng chúng ta có rất nhiều nơ-ron đến mức mạng lưới có thể lưu trữ tất cả hình ảnh huấn luyện của chúng ta trong đó và sau đó nhận dạng chúng bằng cách so khớp mẫu. Mô hình này sẽ hoàn toàn không hoạt động trên dữ liệu thực. Mạng nơron phải bị hạn chế phần nào để buộc mạng này khái quát hoá những gì học được trong quá trình huấn luyện.

Nếu có rất ít dữ liệu huấn luyện, thì ngay cả một mạng lưới nhỏ cũng có thể học thuộc lòng và bạn sẽ thấy hiện tượng "quá khớp". Nói chung, bạn luôn cần nhiều dữ liệu để huấn luyện mạng nơ-ron.

Cuối cùng, nếu bạn đã làm mọi thứ theo đúng quy trình, thử nghiệm với nhiều kích thước mạng để đảm bảo các mức độ tự do của mạng bị hạn chế, áp dụng tính năng loại bỏ và huấn luyện trên nhiều dữ liệu, thì bạn vẫn có thể gặp phải tình trạng không thể cải thiện hiệu suất. Điều này có nghĩa là mạng nơ-ron của bạn, ở hình dạng hiện tại, không thể trích xuất thêm thông tin từ dữ liệu của bạn, như trong trường hợp của chúng ta ở đây.

Bạn có nhớ cách chúng ta sử dụng hình ảnh, được làm phẳng thành một vectơ duy nhất không? Đó là một ý tưởng rất tồi. Các chữ số viết tay được tạo thành từ các hình dạng và chúng tôi đã loại bỏ thông tin về hình dạng khi làm phẳng các pixel. Tuy nhiên, có một loại mạng nơ-ron có thể tận dụng thông tin về hình dạng: mạng tích chập. Hãy thử những cách này.

Nếu bạn gặp khó khăn, sau đây là giải pháp tại thời điểm này:

keras_03_mnist_dense_lrdecay_dropout.ipynb

Tóm tắt

Nếu đã biết tất cả các thuật ngữ được in đậm trong đoạn văn tiếp theo, bạn có thể chuyển sang bài tập tiếp theo. Nếu bạn chỉ mới bắt đầu tìm hiểu về mạng nơ-ron tích chập, vui lòng đọc tiếp.

convolutional.gif

Hình minh hoạ: lọc một hình ảnh bằng 2 bộ lọc liên tiếp, mỗi bộ lọc có 4x4x3=48 trọng số có thể học được.

Đây là giao diện của một mạng nơ-ron tích chập đơn giản trong Keras:

model = tf.keras.Sequential([
    tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1)),
    tf.keras.layers.Conv2D(kernel_size=3, filters=12, activation='relu'),
    tf.keras.layers.Conv2D(kernel_size=6, filters=24, strides=2, activation='relu'),
    tf.keras.layers.Conv2D(kernel_size=6, filters=32, strides=2, activation='relu'),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(10, activation='softmax')
])

Trong một lớp của mạng lưới tích chập, một "nơ-ron" sẽ tính tổng có trọng số của các pixel ngay phía trên nó, chỉ trong một vùng nhỏ của hình ảnh. Nó thêm một độ lệch và truyền tổng qua một hàm kích hoạt, giống như một nơ-ron trong lớp dày đặc thông thường. Sau đó, thao tác này được lặp lại trên toàn bộ hình ảnh bằng cách sử dụng cùng trọng số. Hãy nhớ rằng trong các lớp dày đặc, mỗi nơ-ron đều có trọng số riêng. Ở đây, một "mảng" trọng số duy nhất sẽ trượt trên hình ảnh theo cả hai hướng (một "tích chập"). Đầu ra có nhiều giá trị bằng số lượng pixel trong hình ảnh (mặc dù cần có một số khoảng đệm ở các cạnh). Đây là một thao tác lọc. Trong hình minh hoạ ở trên, bộ lọc này sử dụng 4x4x3=48 trọng số.

Tuy nhiên, 48 trọng số sẽ không đủ. Để tăng thêm mức độ tự do, chúng ta lặp lại thao tác tương tự với một nhóm trọng số mới. Thao tác này sẽ tạo ra một nhóm đầu ra bộ lọc mới. Hãy gọi đó là "kênh" đầu ra theo cách tương tự như các kênh R,G,B trong hình ảnh đầu vào.

Screen Shot 2016-07-29 at 16.02.37.png

Bạn có thể cộng hai (hoặc nhiều) nhóm trọng số thành một tensor bằng cách thêm một phương diện mới. Điều này cho chúng ta hình dạng chung của tensor trọng số cho một lớp tích chập. Vì số lượng kênh đầu vào và đầu ra là các tham số, nên chúng ta có thể bắt đầu xếp chồng và liên kết các lớp tích chập.

Hình minh hoạ: mạng nơ-ron tích chập chuyển đổi "các khối" dữ liệu thành "các khối" dữ liệu khác.

Tích chập có bước sải, gộp tối đa

Bằng cách thực hiện các phép tích chập với bước sải là 2 hoặc 3, chúng ta cũng có thể thu nhỏ khối dữ liệu kết quả theo chiều ngang. Có 2 cách phổ biến để thực hiện việc này:

  • Phép tích chập có bước sải: bộ lọc trượt như trên nhưng có bước sải > 1
  • Gộp tối đa: một cửa sổ trượt áp dụng thao tác MAX (thường là trên các bản vá 2x2, lặp lại sau mỗi 2 pixel)

Minh hoạ: việc trượt cửa sổ tính toán 3 pixel sẽ dẫn đến ít giá trị đầu ra hơn. Các phép tích chập có bước sải hoặc gộp tối đa (tối đa trên cửa sổ 2x2 trượt theo bước sải là 2) là một cách để thu nhỏ khối dữ liệu theo chiều ngang.

Lớp cuối cùng

Sau lớp tích chập cuối cùng, dữ liệu sẽ ở dạng "khối". Có hai cách để truyền dữ liệu qua lớp dày đặc cuối cùng.

Cách đầu tiên là làm phẳng khối dữ liệu thành một vectơ, sau đó đưa vectơ đó vào lớp softmax. Đôi khi, bạn thậm chí có thể thêm một lớp dày đặc trước lớp softmax. Điều này thường tốn kém về số lượng trọng số. Một lớp dày đặc ở cuối mạng tích chập có thể chứa hơn một nửa trọng số của toàn bộ mạng nơron.

Thay vì sử dụng một lớp dày đặc tốn kém, chúng ta cũng có thể chia "khối" dữ liệu đến thành nhiều phần như số lượng lớp, tính giá trị trung bình của các phần đó và truyền các giá trị này qua một hàm kích hoạt softmax. Cách tạo phần đầu phân loại này không tốn trọng số. Trong Keras, có một lớp cho việc này: tf.keras.layers.GlobalAveragePooling2D().

Chuyển sang phần tiếp theo để xây dựng một mạng lưới tích chập cho vấn đề hiện tại.

Hãy cùng xây dựng một mạng nơ-ron tích chập để nhận dạng chữ số viết tay. Chúng ta sẽ sử dụng 3 lớp tích chập ở trên cùng, lớp đọc softmax truyền thống ở dưới cùng và kết nối chúng bằng một lớp kết nối đầy đủ:

Lưu ý rằng lớp tích chập thứ hai và thứ ba có bước sải là 2, điều này giải thích lý do tại sao các lớp này giảm số lượng giá trị đầu ra từ 28x28 xuống 14x14 rồi 7x7.

Hãy viết mã Keras.

Bạn cần đặc biệt chú ý trước lớp tích chập đầu tiên. Thật vậy, mô hình này dự kiến sẽ có một "khối" dữ liệu 3D, nhưng cho đến nay, tập dữ liệu của chúng tôi đã được thiết lập cho các lớp dày đặc và tất cả các pixel của hình ảnh đều được làm phẳng thành một vectơ. Chúng ta cần định hình lại chúng thành hình ảnh 28x28x1 (1 kênh cho hình ảnh thang độ xám):

tf.keras.layers.Reshape(input_shape=(28*28,), target_shape=(28, 28, 1))

Bạn có thể sử dụng dòng này thay cho lớp tf.keras.layers.Input mà bạn đã có cho đến nay.

Trong Keras, cú pháp cho lớp tích chập được kích hoạt "relu" là:

tf.keras.layers.Conv2D(kernel_size=3, filters=12, padding='same', activation='relu')

Đối với một phép tích chập có bước sải, bạn sẽ viết:

tf.keras.layers.Conv2D(kernel_size=6, filters=24, padding='same', activation='relu', strides=2)

Để làm phẳng một khối dữ liệu thành một vectơ để có thể được sử dụng bởi một lớp dày đặc:

tf.keras.layers.Flatten()

Đối với lớp dày đặc, cú pháp không thay đổi:

tf.keras.layers.Dense(200, activation='relu')

Mô hình của bạn có vượt qua ngưỡng chính xác 99% không? Gần đúng... nhưng hãy xem xét đường cong tổn thất xác thực. Bạn có thấy quen không?

Ngoài ra, hãy xem các dự đoán. Lần đầu tiên, bạn sẽ thấy rằng hầu hết 10.000 chữ số kiểm thử hiện đã được nhận dạng chính xác. Chỉ còn khoảng 4,5 hàng phát hiện sai (khoảng 110 chữ số trong số 10.000)

Nếu bạn gặp khó khăn, sau đây là giải pháp tại thời điểm này:

keras_04_mnist_convolutional.ipynb

Quá trình huấn luyện trước đó cho thấy rõ các dấu hiệu của việc khớp quá mức (và vẫn chưa đạt được độ chính xác 99%). Chúng ta có nên thử lại với dropout không?

Lần này kết quả thế nào?

Có vẻ như phương pháp giảm số lượng nơ-ron đã có hiệu quả lần này. Mức tổn thất xác thực không tăng lên nữa và độ chính xác cuối cùng phải cao hơn 99%. Xin chúc mừng!

Lần đầu tiên thử áp dụng dropout, chúng tôi nghĩ rằng mình gặp vấn đề về việc khớp quá mức, trong khi thực tế vấn đề nằm ở cấu trúc của mạng nơ-ron. Chúng ta không thể đi xa hơn nếu không có các lớp tích chập và không có cách nào để loại bỏ điều đó.

Lần này, có vẻ như việc khớp quá mức là nguyên nhân gây ra vấn đề và việc loại bỏ thực sự đã giúp ích. Hãy nhớ rằng có nhiều yếu tố có thể gây ra sự khác biệt giữa các đường cong tổn thất trong quá trình huấn luyện và xác thực, trong đó tổn thất xác thực tăng lên. Việc khớp quá mức (mạng sử dụng quá nhiều bậc tự do một cách không hiệu quả) chỉ là một trong số đó. Nếu tập dữ liệu quá nhỏ hoặc cấu trúc mạng nơ-ron không đầy đủ, bạn có thể thấy hành vi tương tự trên các đường cong tổn thất, nhưng việc loại bỏ sẽ không giúp ích.

Cuối cùng, hãy thử thêm chuẩn hoá theo lô.

Đó là lý thuyết, trên thực tế, bạn chỉ cần nhớ một vài quy tắc:

Hãy tuân theo quy tắc và thêm một lớp chuẩn hoá theo lô vào mỗi lớp mạng nơ-ron, ngoại trừ lớp cuối cùng. Đừng thêm nó vào lớp "softmax" cuối cùng. Điều này sẽ không hữu ích.

# Modify each layer: remove the activation from the layer itself.
# Set use_bias=False since batch norm will play the role of biases.
tf.keras.layers.Conv2D(..., use_bias=False),
# Batch norm goes between the layer and its activation.
# The scale factor can be turned off for Relu activation.
tf.keras.layers.BatchNormalization(scale=False, center=True),
# Finish with the activation.
tf.keras.layers.Activation('relu'),

Độ chính xác hiện tại là bao nhiêu?

Với một chút điều chỉnh (BATCH_SIZE=64, tham số giảm tốc độ học 0,666, tốc độ loại bỏ trên lớp dày đặc 0,3) và một chút may mắn, bạn có thể đạt được 99,5%. Việc điều chỉnh tốc độ học và hệ số loại bỏ được thực hiện theo "các phương pháp hay nhất" để sử dụng chuẩn hoá theo lô:

  • Chuẩn hoá theo lô giúp các mạng nơ-ron hội tụ và thường cho phép bạn huấn luyện nhanh hơn.
  • Chuẩn hoá theo lô là một phương pháp điều chỉnh. Bạn thường có thể giảm số lượng dropout mà bạn sử dụng, hoặc thậm chí không sử dụng dropout.

Sổ tay giải pháp có lượt huấn luyện 99,5%:

keras_05_mnist_batch_norm.ipynb

Bạn sẽ tìm thấy phiên bản mã sẵn sàng cho đám mây trong thư mục mlengine trên GitHub, cùng với hướng dẫn chạy mã này trên Nền tảng AI của Google Cloud. Trước khi chạy phần này, bạn sẽ phải tạo một tài khoản Google Cloud và bật tính năng thanh toán. Các tài nguyên cần thiết để hoàn thành phòng thí nghiệm này có giá chưa đến vài đô la (giả sử thời gian huấn luyện là 1 giờ trên một GPU). Cách chuẩn bị tài khoản:

  1. Tạo một dự án Google Cloud Platform (http://cloud.google.com/console).
  2. Bật tính năng thanh toán.
  3. Cài đặt công cụ dòng lệnh GCP (GCP SDK tại đây).
  4. Tạo một bộ chứa Google Cloud Storage (đặt trong khu vực us-central1). Bộ chứa này sẽ được dùng để dàn dựng mã huấn luyện và lưu trữ mô hình đã huấn luyện của bạn.
  5. Bật các API cần thiết và yêu cầu hạn mức cần thiết (chạy lệnh đào tạo một lần và bạn sẽ nhận được thông báo lỗi cho biết những gì cần bật).

Bạn đã xây dựng mạng nơ-ron đầu tiên và huấn luyện mạng này cho đến khi đạt độ chính xác 99%. Các kỹ thuật đã học được không dành riêng cho tập dữ liệu MNIST, mà thực tế là chúng được sử dụng rộng rãi khi làm việc với mạng nơ-ron. Để cảm ơn bạn đã tham gia, đây là thẻ "ghi chú tóm tắt" cho phòng thí nghiệm, ở dạng hoạt hình. Bạn có thể dùng tính năng này để ghi nhớ những gì mình đã học:

cliffs notes tensorflow lab.png

Các bước tiếp theo

  • Sau mạng nơ-ron tích chập và mạng được kết nối hoàn toàn, bạn nên xem xét mạng nơ-ron hồi quy.
  • Để chạy hoạt động huấn luyện hoặc suy luận trên đám mây trên một cơ sở hạ tầng phân tán, Google Cloud cung cấp AI Platform.
  • Cuối cùng, chúng tôi rất mong nhận được ý kiến phản hồi. Vui lòng cho chúng tôi biết nếu bạn thấy có điều gì sai sót trong phòng thí nghiệm này hoặc nếu bạn nghĩ rằng chúng tôi nên cải thiện. Chúng tôi xử lý ý kiến phản hồi thông qua các vấn đề trên GitHub [đường liên kết phản hồi].

HR.png

Martin Görner ID small.jpg

Tác giả: Martin Görner

Twitter: @martin_gorner

Tất cả hình ảnh hoạt hình trong phòng thí nghiệm này đều có bản quyền: alexpokusay / 123RF stock photos