Random Forest để phân loại hình ảnh bằng OpenCV và Python

Tìm hiểu cách sử dụng Random Forest, một thuật toán học tổng hợp mạnh mẽ, để phân loại hình ảnh bằng OpenCV và Python.

Nhắc nhở về cách hoạt động của rừng ngẫu nhiên

Chủ đề xung quanh thuật toán Rừng ngẫu nhiên đã được Jason Brownlee giải thích rõ ràng trong các hướng dẫn này [1, 2], nhưng trước tiên hãy bắt đầu bằng việc tóm tắt một số điểm quan trọng nhất:

  • Random Forest là một loại thuật toán học máy tổng hợp được gọi là đóng gói. Nó là một biến thể phổ biến của cây quyết định được đóng gói
  • Cây quyết định là một mô hình phân nhánh bao gồm một hệ thống phân cấp các nút quyết định, trong đó mỗi nút quyết định phân chia dữ liệu dựa trên quy tắc quyết định. Huấn luyện cây quyết định bao gồm việc lựa chọn tham lam các điểm phân chia tốt nhất (tức là các điểm phân chia không gian đầu vào tốt nhất) bằng cách giảm thiểu hàm chi phí. 
  • Cách tiếp cận tham lam mà qua đó cây quyết định xây dựng ranh giới quyết định của chúng khiến chúng dễ bị ảnh hưởng bởi phương sai cao. Điều này có nghĩa là những thay đổi nhỏ trong tập dữ liệu huấn luyện có thể dẫn đến các cấu trúc cây rất khác nhau và từ đó dẫn đến các dự đoán mô hình. Nếu cây quyết định không được cắt tỉa, nó cũng sẽ có xu hướng thu được nhiễu và các giá trị ngoại lệ trong dữ liệu huấn luyện. Sự nhạy cảm này đối với dữ liệu huấn luyện làm cho cây quyết định dễ bị trang bị quá mức. 
  • Cây quyết định được đóng gói giải quyết tính nhạy cảm này bằng cách kết hợp các dự đoán từ nhiều cây quyết định, mỗi cây được huấn luyện trên một mẫu khởi động của tập dữ liệu huấn luyện được tạo bằng cách lấy mẫu tập dữ liệu có thay thế. Hạn chế của cách tiếp cận này xuất phát từ thực tế là cùng một cách tiếp cận tham lam huấn luyện từng cây và một số mẫu có thể được chọn nhiều lần trong quá trình huấn luyện, khiến cho rất có thể các cây có chung điểm phân chia (hoặc giống nhau) (do đó, dẫn đến cây tương quan). 
  • Thuật toán Rừng ngẫu nhiên cố gắng giảm thiểu mối tương quan này bằng cách huấn luyện từng cây trên một tập hợp con ngẫu nhiên của dữ liệu huấn luyện, được tạo bằng cách lấy mẫu ngẫu nhiên tập dữ liệu mà không thay thế. Theo cách này, thuật toán tham lam chỉ có thể xem xét một tập hợp con cố định của dữ liệu để tạo ra các điểm phân chia tạo nên mỗi cây, điều này buộc các cây phải khác nhau. 
  • Trong trường hợp có vấn đề về phân loại, mỗi cây trong rừng tạo ra một đầu ra dự đoán và nhãn lớp cuối cùng được xác định là đầu ra mà phần lớn các cây đã tạo ra. Trong trường hợp hồi quy, đầu ra cuối cùng là giá trị trung bình của các đầu ra do tất cả các cây tạo ra. 

Áp dụng thuật toán rừng ngẫu nhiên để phân loại hình ảnh

Nghiên cứu điển hình về tiền giấy

Trước tiên, chúng ta sẽ sử dụng tập dữ liệu tiền giấy được sử dụng trong hướng dẫn này

Bộ dữ liệu tiền giấy là một bộ dữ liệu tương đối đơn giản liên quan đến việc dự đoán tính xác thực của một tờ tiền nhất định. Tập dữ liệu chứa 1.372 hàng, mỗi hàng biểu thị một vectơ đặc trưng bao gồm bốn số đo khác nhau được trích xuất từ ​​ảnh tiền giấy, cộng với nhãn lớp tương ứng của nó (xác thực hoặc không). 

Các giá trị trong mỗi vectơ đặc trưng tương ứng như sau:

  1. Phương sai của ảnh biến đổi Wavelet (liên tục)
  2. Độ lệch của hình ảnh được biến đổi Wavelet (liên tục)
  3. Kurtosis của hình ảnh biến đổi Wavelet (liên tục)
  4. Entropy của hình ảnh (liên tục)
  5. Nhãn lớp (số nguyên)

Có thể tải xuống tập dữ liệu từ Kho lưu trữ máy học UCI

Như trong hướng dẫn của Jason, chúng ta sẽ tải tập dữ liệu, chuyển đổi số chuỗi của nó thành số float và phân chia nó thành các tập huấn luyện và kiểm tra:


# Function to load the dataset
def load_csv(filename):
    file = open(filename, "rt")
    lines = reader(file)
    dataset = list(lines)
    return dataset
 
# Function to convert a string column to float
def str_column_to_float(dataset, column):
    for row in dataset:
        row[column] = float32(row[column].strip())
 
# Load the dataset from text file
data = load_csv('Data/data_banknote_authentication.txt')
 
# Convert the dataset string numbers to float
for i in range(len(data[0])):
    str_column_to_float(data, i)
 
# Convert list to array
data = array(data)
 
# Separate the dataset samples from the ground truth
samples = data[:, :4]
target = data[:, -1, newaxis].astype(int32)
 
# Split the data into training and testing sets
x_train, x_test, y_train, y_test = ms.train_test_split(samples, target, test_size=0.2, random_state=10)

Thư viện OpenCV triển khai hàm RTrees_create trong mô-đun ml, hàm này sẽ cho phép chúng ta tạo một cây quyết định trống:

# Create an empty decision tree
rtree = ml.RTrees_create()

Tất cả các cây trong rừng sẽ được huấn luyện với cùng các giá trị tham số, mặc dù trên các tập con khác nhau của tập dữ liệu huấn luyện. Các giá trị tham số mặc định có thể được tùy chỉnh, nhưng trước tiên hãy làm việc với cách triển khai mặc định. Chúng ta sẽ sớm quay lại việc tùy chỉnh các giá trị tham số này trong phần tiếp theo:


# Train the decision tree
rtree.train(x_train, ml.ROW_SAMPLE, y_train)
 
# Predict the target labels of the testing data
_, y_pred = rtree.predict(x_test)
 
# Compute and print the achieved accuracy
accuracy = (sum(y_pred.astype(int32) == y_test) / y_test.size) * 100
print('Accuracy:', accuracy[0], '%')
Accuracy: 96.72727272727273 %

Chúng tôi đã đạt được độ chính xác cao khoảng 96,73% bằng cách sử dụng triển khai mặc định thuật toán Rừng ngẫu nhiên trên tập dữ liệu tiền giấy. 

Danh sách mã đầy đủ như sau:


from csv import reader
from numpy import array, float32, int32, newaxis
from cv2 import ml
from sklearn import model_selection as ms
 
# Function to load the dataset
def load_csv(filename):
    file = open(filename, "rt")
    lines = reader(file)
    dataset = list(lines)
    return dataset
 
# Function to convert a string column to float
def str_column_to_float(dataset, column):
    for row in dataset:
        row[column] = float32(row[column].strip())
 
 
# Load the dataset from text file
data = load_csv('Data/data_banknote_authentication.txt')
 
# Convert the dataset string numbers to float
for i in range(len(data[0])):
    str_column_to_float(data, i)
 
# Convert list to array
data = array(data)
 
# Separate the dataset samples from the ground truth
samples = data[:, :4]
target = data[:, -1, newaxis].astype(int32)
 
# Split the data into training and testing sets
x_train, x_test, y_train, y_test = ms.train_test_split(samples, target, test_size=0.2, random_state=10)
 
# Create an empty decision tree
rtree = ml.RTrees_create()
 
# Train the decision tree
rtree.train(x_train, ml.ROW_SAMPLE, y_train)
 
# Predict the target labels of the testing data
_, y_pred = rtree.predict(x_test)
 
# Compute and print the achieved accuracy
accuracy = (sum(y_pred.astype(int32) == y_test) / y_test.size) * 100
print('Accuracy:', accuracy[0], '%')

Nghiên cứu điển hình về chữ số

Hãy cân nhắc việc áp dụng Rừng ngẫu nhiên cho các hình ảnh từ tập dữ liệu chữ số của OpenCV. 

Tập dữ liệu chữ số vẫn còn tương đối đơn giản. Tuy nhiên, các vectơ đặc trưng mà chúng tôi sẽ trích xuất từ ​​hình ảnh của nó bằng phương pháp HOG sẽ có số chiều cao hơn (81 đặc điểm) so với các vectơ trong tập dữ liệu tiền giấy. Vì lý do này, chúng ta có thể coi tập dữ liệu chữ số tương đối khó làm việc hơn so với tập dữ liệu tiền giấy. 

Trước tiên, chúng tôi sẽ điều tra cách triển khai mặc định của thuật toán Rừng ngẫu nhiên đối phó với dữ liệu có chiều cao hơn:


from digits_dataset import split_images, split_data
from feature_extraction import hog_descriptors
from numpy import array, float32
from cv2 import ml
 
 
# Load the digits image
img, sub_imgs = split_images('Images/digits.png', 20)
 
# Obtain training and testing datasets from the digits image
digits_train_imgs, digits_train_labels, digits_test_imgs, digits_test_labels = split_data(20, sub_imgs, 0.8)
 
# Convert the image data into HOG descriptors
digits_train_hog = hog_descriptors(digits_train_imgs)
digits_test_hog = hog_descriptors(digits_test_imgs)
 
# Create an empty decision tree
rtree_digits = ml.RTrees_create()
 
# Predict the target labels of the testing data
_, digits_test_pred = rtree_digits.predict(digits_test_hog)
 
# Compute and print the achieved accuracy
accuracy_digits = (sum(digits_test_pred.astype(int) == digits_test_labels) / digits_test_labels.size) * 100
print('Accuracy:', accuracy_digits[0], '%')
Accuracy: 81.0 %

Chúng tôi thấy rằng việc triển khai mặc định trả về độ chính xác là 81%. 

Sự giảm độ chính xác này so với mức đạt được trên tập dữ liệu tiền giấy có thể cho thấy rằng khả năng triển khai mặc định của mô hình có thể không đủ để tìm hiểu mức độ phức tạp của dữ liệu nhiều chiều hơn mà chúng tôi hiện đang làm việc. 

Hãy điều tra xem liệu chúng ta có thể cải thiện độ chính xác hay không bằng cách thay đổi:

  • Tiêu chí kết thúc của thuật toán huấn luyện, xem xét số lượng cây trong rừng và hiệu suất ước tính của mô hình được đo bằng lỗi Out-Of-Bag (OOB). Tiêu chí chấm dứt hiện tại có thể được tìm thấy bằng cách sử dụng phương thức getTermCriteria và thiết lập bằng phương thức setTermCriteria. Khi sử dụng cái sau, số lượng cây có thể được đặt thông qua tham số TERM_CRITERIA_MAX_ITER, trong khi độ chính xác mong muốn có thể được chỉ định bằng tham số TERM_CRITERIA_EPS
  • Độ sâu tối đa có thể mà mỗi cây trong rừng có thể đạt được. Độ sâu hiện tại có thể được tìm thấy bằng phương pháp getMaxDepth và thiết lập bằng phương pháp setMaxDepth. Độ sâu cây được chỉ định có thể không đạt được nếu đáp ứng tiêu chí kết thúc ở trên trước.

Khi điều chỉnh các tham số trên, hãy nhớ rằng việc tăng số lượng cây có thể tăng khả năng của mô hình trong việc nắm bắt được nhiều chi tiết phức tạp hơn trong dữ liệu huấn luyện; nó cũng sẽ tăng thời gian dự đoán một cách tuyến tính và làm cho mô hình dễ bị khớp quá mức. Do đó, hãy điều chỉnh các thông số một cách thận trọng. 

Nếu chúng ta thêm vào các dòng sau sau khi tạo cây quyết định trống, chúng ta có thể tìm thấy các giá trị mặc định của độ sâu của cây cũng như các tiêu chí kết thúc:

print('Default tree depth:', rtree_digits.getMaxDepth())
print('Default termination criteria:', rtree_digits.getTermCriteria())
Default tree depth: 5
Default termination criteria: (3, 50, 0.1)

Theo cách này, chúng ta có thể thấy rằng, theo mặc định, mỗi cây trong rừng có độ sâu (hoặc số cấp) bằng 5, trong khi số lượng cây và độ chính xác mong muốn được đặt thành 50 và 0,1, tương ứng. Giá trị đầu tiên được trả về bởi phương thức getTermCriteria đề cập đến type của tiêu chí chấm dứt đang được xem xét, trong đó giá trị 3 chỉ định việc chấm dứt dựa trên cả TERM_CRITERIA_MAX_ITERTERM_CRITERIA_EPS.

Bây giờ hãy thử thay đổi các giá trị được đề cập ở trên để điều tra ảnh hưởng của chúng đến độ chính xác của dự đoán. Danh sách mã như sau:


from digits_dataset import split_images, split_data
from feature_extraction import hog_descriptors
from numpy import array, float32
from cv2 import ml, TERM_CRITERIA_MAX_ITER, TERM_CRITERIA_EPS
 
 
# Load the digits image
img, sub_imgs = split_images('Images/digits.png', 20)
 
# Obtain training and testing datasets from the digits image
digits_train_imgs, digits_train_labels, digits_test_imgs, digits_test_labels = split_data(20, sub_imgs, 0.8)
 
# Convert the image data into HOG descriptors
digits_train_hog = hog_descriptors(digits_train_imgs)
digits_test_hog = hog_descriptors(digits_test_imgs)
 
# Create an empty decision tree
rtree_digits = ml.RTrees_create()
 
# Read the default parameter values
print('Default tree depth:', rtree_digits.getMaxDepth())
print('Default termination criteria:', rtree_digits.getTermCriteria())
 
# Change the default parameter values
rtree_digits.setMaxDepth(15)
rtree_digits.setTermCriteria((TERM_CRITERIA_MAX_ITER + TERM_CRITERIA_EPS, 100, 0.01))
 
# Train the decision tree
rtree_digits.train(digits_train_hog.astype(float32), ml.ROW_SAMPLE, digits_train_labels)
 
# Predict the target labels of the testing data
_, digits_test_pred = rtree_digits.predict(digits_test_hog)
 
# Compute and print the achieved accuracy
accuracy_digits = (sum(digits_test_pred.astype(int) == digits_test_labels) / digits_test_labels.size) * 100
print('Accuracy:', accuracy_digits[0], ‘%')
Accuracy: 94.1 %

Chúng ta có thể thấy rằng các giá trị tham số mới được đặt sẽ nâng độ chính xác của dự đoán lên 94,1%.

Các giá trị tham số này đang được đặt tùy ý ở đây để minh họa cho ví dụ này. Tuy nhiên, chúng tôi luôn khuyên bạn nên thực hiện một cách tiếp cận có hệ thống hơn để điều chỉnh các tham số của mô hình và điều tra xem mỗi tham số ảnh hưởng đến hiệu suất của nó như thế nào. 

#python  #opencv  

Random Forest để phân loại hình ảnh bằng OpenCV và Python
1.50 GEEK