Nhận dạng tiền Việt Nam với Transfer Learning (VGG16 CNN Classify)

Xin chào toàn thể anh em Mì AI, rất vui lại được gặp lại anh em. Hôm nay chúng ta sẽ cùng nhau làm bài toán nhận dạng tiền Việt Nam bằng CNN Classify.

Nhiệm vụ của bài toán là: khi chúng ta đưa tiền vào camera, hệ thống phải nhận diện mệnh giá của từng tờ tiền. Bài này có nhiều cách làm lắm, và khi mình đăng bài này lên thế nào cũng có bạn vào bảo :”Cái này đơn giản mà, OpenCV, tách màu HSV là xong cần gì VGG16 cho mệt”.

Đúng! Nhưng lại sai vì mục đích bài này để các bạn nắm được các món:

  • Tự tạo dữ liệu cho bài toán
  • Augment dữ liệu tránh Overfit cho model
  • Cách “thiết kế” mạng NN cho bài toán dựa trên khung nền của mạng thần thánh VGG16.

Và một lần nữa lại xin nhắc lại, bài toán này làm để học tập, các bạn khi sử dụng làm thương mại cần làm nhiều thứ khác nữa để tránh các bạn comment kiểu “Cái này đếm nhầm bỏ bố”, “Dùng được còn khướt :D”.

Okie! Let’s go!

Các bạn hãy tạo thư mục MiAI_Money_Classify, sau đó chuyển vào trong thư mục đó bằng lệnh “cd” và gõ lệnh:

Nếu các bạn cần source hãy clone từ github của mình bằng lệnh:

git clone https://github.com/thangnch/MiAI_Money_ClassifyCode language: PHP (php)

Và khi clone xong thì nhớ chạy lệnh cài đặt thư viện bằng lệnh:

pip install -r setup.txtCode language: CSS (css)

Mình đã tạo sẵn dữ liệu kèm với file pix.data và weight pretrain trong link này, dành cho các bạn “ngại” tự làm nhá, haha!

Phần 1 – Phân tích bài toán

Dự kiến thực hiện sẽ gồm các bước như sau:

  • Tạo dữ liệu bằng cách đọc ảnh các tờ tiền từ camera
  • Thiết kế mạng NN với đầu vào là ảnh (128,128,3), đưa vào mạng VGG16 và đầu ra của VGG16 sẽ dùng để đưa vào 1 mạng NN nhỏ kết thúc bằng 1 lớp Dense và hàm softmax.
  • Đầu ra sẽ là 1 vector softmax chứa các probality p(i) ứng với mỗi class i, chúng ta sẽ in ra giá trị max trong vector đó và chọn đó làm class dự đoán.

Vấn đề về dữ liệu

Ở đây mình dự kiến làm sample với 3 class là 10000,20000 và 50000. Vậy theo suy nghĩ thông thường thì chỉ cần chuẩn bị dữ liệu cho 3 class và lớp Dense output cuối cùng sẽ là Dense(3).

Tuy nhiên do tổng các p(i) phải = 1 nên giả sử khi chúng ta không cầm đồng tiền nào thì máy sẽ buộc phải dự đoán vào 1 trong 3 class nói trên, dó là điều vô lý. Vì vậy chúng ta sẽ train với 4 class output là “Không tiền”,”10000″,”20000″ và “50000”, output sẽ là Dense(4).

Phần 2 – Tạo dữ liệu cho bài toán

Có bài toán thì có dữ liệu cho trước rất nhiều trên mạng, ví dụ face, ví dụ ảnh đồ vật, ảnh phong cảnh. Tuy nhiên các dữ liệu về tiền, đặc biệt tiền Việt thì tìm mãi chả ra nên phải tự tạo cho mình.

Cách tạo dữ liệu đơn giản là viết một đoạn python đọc liên tục từ camera và save lại vào các thư mục tương ứng ảnh các tờ tiền. Ví dụ trong bài này mình làm sample với 3 class là 10000,20000 và 50000 nhé (các bạn chú ý sửa cái dòng label = “00000” thành class mà mình muốn capture dữ liệu )

# Label: 00000 là ko cầm tiền, còn lại là các mệnh giá
label = "00000"

cap = cv2.VideoCapture(0)

# Biến đếm, để chỉ lưu dữ liệu sau khoảng 60 frame, tránh lúc đầu chưa kịp cầm tiền lên
i=0
while(True):
    # Capture frame-by-frame
    #
    i+=1
    ret, frame = cap.read()
    if not ret:
        continue
    frame = cv2.resize(frame, dsize=None,fx=0.3,fy=0.3)

    # Hiển thị
    cv2.imshow('frame',frame)

    # Lưu dữ liệu
    if i>=60:
        # Tạo thư mục nếu chưa có
        if not os.path.exists('data/' + str(label)):
            os.mkdir('data/' + str(label))
            
        cv2.imwrite('data/' + str(label) + "/" + str(i) + ".png",frame)
Code language: PHP (php)

Sau khi chạy thành công 4 class, chúng ta sẽ có thư mục data với các subfolders như sau (chú ý tạo thư mục data trước khi chạy nếu không sẽ bị lỗi):

CNN Classify.

Các bạn hãy đảm bảo mỗi class có số ảnh tương đồng nhau nhé, tầm 1000-1500 là okie. Mình có in số ảnh capture được ra màn hình để các bạn theo dõi và stop khi cần thiết nhé ;).

Ví dụ 1 số ảnh data với mỗi class:

CNN Classify.

Như vậy là xong phần tạo dữ liệu. Bây giờ chúng ta sẽ tiến hành xử lý dữ liệu đó và ghi vào file dữ liệu pickle.

Mã nguồn đầy đủ trong file make_data.py nhé cả nhà!

Mình có để sẵn file dữ liệu cho bạn nào lười tạo. Các bạn vào thư mục https://www.miai.vn/thu-vien-mi-ai/ và tìm chữ “tiền Việt Nam” để tải nhé.

Phần 3 – Xử lý dữ liệu ảnh

Các bạn để ý như sau, trong quá trình chúng ta train model, thử nghiệm model thì sẽ phải chạy chương trình rất nhiều lần để debug, sửa lỗi… Như vậy mà mỗi lần chạy lại phải mò hết đống file và thư mục trên thì sẽ khá lâu nên chúng ta sẽ đọc 1 lần và thực hiện:

  • Convert nhãn (là cái mớ 00000,10000,20000….) thành one-hot
  • Resize ảnh về 128×128

và lưu vào file pickle để lần sau load cho tiện. (bạn nào chưa rõ one-hot là gì có thể đọc link này)

def save_data(raw_folder=raw_folder):

    dest_size = (128, 128)
    print("Bắt đầu xử lý ảnh...")

    pixels = []
    labels = []

    # Lặp qua các folder con trong thư mục raw
    for folder in listdir(raw_folder):
        if folder!='.DS_Store':
            print("Folder=",folder)
            # Lặp qua các file trong từng thư mục chứa các em
            for file in listdir(raw_folder  + folder):
                if file!='.DS_Store':
                    print("File=", file)
                    pixels.append( cv2.resize(cv2.imread(raw_folder  + folder +"/" + file),dsize=(128,128)))
                    labels.append( folder)

    pixels = np.array(pixels)
    labels = np.array(labels)#.reshape(-1,1)

    from sklearn.preprocessing import LabelBinarizer
    encoder = LabelBinarizer()
    labels = encoder.fit_transform(labels)
    print(labels)

    file = open('pix.data', 'wb')
    # dump information to that file
    pickle.dump((pixels,labels), file)
    # close the file
    file.close()

    returnCode language: PHP (php)

Sau khi lưu xong thì chúng ta không cần quan tâm đến cái mớ folder data kia nữa mà chỉ quan tâm đến file pix.data mới sinh ra. Nó chứa full cả ảnh và labels rồi.

Khi nào cần dùng thì load cái vèo trong nốt nhạc:

def load_data():
    file = open('pix.data', 'rb')

    # dump information to that file
    (pixels, labels) = pickle.load(file)

    # close the file
    file.close()

    print(pixels.shape)
    print(labels.shape)


    return pixels, labelsCode language: PHP (php)

Phần 4 – Thiết kế mạng CNN Classify dùng để train

Chúng ta chuẩn bị dữ liệu xong, xử lý xong thì giờ là phần quan trọng nhất – thiết kế mạng để train. Làm deep mà ko có mạng thì chả có gì hết 😀

Chúng ta đã biết cấu trúc của một mạng CNN, cụ thể là VGG16 sẽ có dạng như sau:

CNN Classify vgg16
Nguồn: Tại đây

Nhìn vào cấu trúc VGG16 sẽ gồm 2 phần, phần màu cam là trích đặt trưng của ảnh còn phần màu tím sẽ là các lớp FC để classify. Nhưng classify ở đây là dùng cho mục đích của người ta, ko phải cho bài toán nhận dạng tiền của chúng ta nên chúng ta sẽ là gì….?

Chúng ta sẽ chặt phăng cái phần tím đó vất đi bằng cách khai báo include_top=False khi implement cái mạng này:

 model_vgg16_conv = VGG16(weights='imagenet', include_top=False)Code language: PHP (php)

Rồi bây giờ chúng ta thực hiện ghép nối cái FC của chúng ta vào bằng đoạn lệnh:

# Đóng băng các layers
    for layer in model_vgg16_conv.layers:
        layer.trainable = False

    # Tạo model với input là ảnh, lấy output của VGG16 và làm input của các layers FC thêm vào
    input = Input(shape=(128, 128, 3), name='image_input')
    output_vgg16_conv = model_vgg16_conv(input)

    # Thêm vào các FC layers 
    x = Flatten(name='flatten')(output_vgg16_conv)
    x = Dense(4096, activation='relu', name='fc1')(x)
    x = Dropout(0.5)(x)
    x = Dense(4096, activation='relu', name='fc2')(x)
    x = Dropout(0.5)(x)
    x = Dense(4, activation='softmax', name='predictions')(x)Code language: PHP (php)

Chú ý ở đây phải có đoạn lệnh # Đóng băng các layers nhé, đoạn này nghĩa là khi train ta chỉ điều chỉnh weights của phần FC thêm vào, ko sờ mó gì đến mạng VGG đã lấy vì họ train tốt lắm rồi.

Chú ý layers cuối cùng là Dense(4) với activation softmax như chúng ta phân tích ở trên. Mạng cuối cùng sẽ như sau, phần màu xanh lá cây là lớp FC mà chúng ta thêm vào, output sẽ là 1 vector chứ probality từng class.

CNN Classify.

Phần 5 – Sử dụng augmentation cho dữ liệu

Bây giờ nếu chúng ta sử dụng ngay ảnh nói trên để train cho model CNN Classify thì sẽ bị hiện tượng Overfit vì dữ liệu nhiều nhưng đa phần giống nhau. Dẫn đến train sẽ có chất lượng tốt nhưng khi test sẽ thấy không nhận chuẩn lắm.

Chúng ta sẽ thực hiện augment dữ liệu để làm phong phú hơn dữ liệu, tăng data variance , tăng tính tổng quát cho model bằng ImageDataGenerator của Keras.

Mình đã có một bài chi tiết về món này tại đây, bạn nào chưa biết có thể xem lại.

Trong bài này mình dựa vào thực tế bài toán nên chỉ sử dụng các phép augment như sau:

aug = ImageDataGenerator(rotation_range=20, zoom_range=0.1,
    rescale=1./255,
	width_shift_range=0.1,
    height_shift_range=0.1,
	horizontal_flip=True,
    brightness_range=[0.2,1.5], fill_mode="nearest")Code language: PHP (php)

Và chúng ta cũng không augment và lưu ra file mà sẽ gen ra ảnh trực tiếp trong quá trình train luôn nhé.

Phần 6 – Train model CNN Classify

Phần này các bạn đọc source trong file train.py nha!

Nếu như mọi lần chúng ta sử dụng lệnh model.fit để train thì lần này chúng ta sẽ sử dụng model.fit_generator . Lý do đơn giản chúng ta sinh ra dữ liệu trong quá trình train mà.

vgghist=vggmodel.fit_generator(aug.flow(X_train, y_train, batch_size=64),
                               epochs=50,
                               validation_data=aug.flow(X_test,y_test,
                               batch_size=len(X_test)),
                               callbacks=callbacks_list)

Các bạn để ý sẽ thấy mình augment cho cả train và val cho nó tăng độ khó cho model “chăm học” chút.

Mình cũng đã tạo checkpoint để lưu lại các weights tốt:

filepath="weights-{epoch:02d}-{val_accuracy:.2f}.hdf5"
checkpoint = ModelCheckpoint(filepath, monitor='val_accuracy', verbose=1, save_best_only=True, mode='max')
callbacks_list = [checkpoint]Code language: PHP (php)

Chắc do dữ liệu mình cũng đơn giản, giới hạn bài toán ở mức để học tập là chính nên model đang validation đạt 0.98. Cơ mà chả biết được có chuẩn không nên cứ phải test một chút thực tế cho lành 😀

Mình có để sẵn weights cho bạn nào không có điều kiện train. Các bạn tải tại đây nhé.

Phần 7 – Thử nghiệm model CNN Classify

Bây giờ các bạn chạy file test.py để kiểm thử model. File này khá đơn giản:

  • Đọc ảnh từ camera
  • Thực hiện resize ảnh, normalize ảnh, chuyển thành tensor và đưa vào model để predict
  • Lấy kết quả đầu ra và hiển thị trên màn hình

Các bạn lưu ý mình sẽ load file weights đã được checkpoint lưu lại ở trên ở đoạn này:

# Load weights model da train
my_model = get_model()
my_model.load_weights("weights-19-1.00.hdf5")Code language: PHP (php)

Đồng thời, chúng ta chỉ xét các ảnh được nhận dạng có probality > 0.8 và hiển thị lên màn hình nhé (tiền bạc là cứ phải chắc chắn, kaka)

# Predict
    predict = my_model.predict(image)
    print("This picture is: ", class_name[np.argmax(predict[0])], (predict[0]))
    print(np.max(predict[0],axis=0))
    if (np.max(predict)>=0.8) and (np.argmax(predict[0])!=0):


        # Show image
        font = cv2.FONT_HERSHEY_SIMPLEX
        org = (50, 50)
        fontScale = 1.5
        color = (0, 255, 0)
        thickness = 2

        cv2.putText(image_org, class_name[np.argmax(predict)], org, font,
                    fontScale, color, thickness, cv2.LINE_AA)Code language: PHP (php)

Nếu mọi thứ các bạn làm okie, thì các bạn sẽ nhận được kết quả như video dưới đây:

Okie rồi, mình đã guide qua các bạn cách sử dụng CNN Classify, transfer learning cho model của mình, sử dụng ImageGenerator để làm giàu dữ liệu tránh OF…

Bài này đưa vào thực tế cần làm thêm nhiều việc như:

  • Train thêm các loại tiền khác
  • Predict trong vài frame liên tục và lấy bình quân có trọng số để tăng độ chính xác, tránh nhận nhầm.
  • Giới hạn vùng xử lý (ví dụ ở giữa khung hinh) để tăng tốc độ xử lý video…

Mình xin tạm dừng bài này CNN Classify ở đây. Bài này mình lấy cảm hứng từ bài post của bạn William Cường trên Forum ML cơ bản, cảm ơn bạn:

machine learning

Hẹn gặp lại các bạn trong các bài tiếp theo nhé!

Hãy join cùng cộng đồng Mì AI nhé!

Fanpage: http://facebook.com/miaiblog
Group trao đổi, chia sẻ: https://www.facebook.com/groups/miaigroup
Website: https://miai.vn
Youtube: http://bit.ly/miaiyoutube

Related Post

27 Replies to “Nhận dạng tiền Việt Nam với Transfer Learning (VGG16 CNN Classify)”

  1. Anh cho em hỏi tại sao lại phải có scale trong aug = 1./255, điều gì sẽ xảy ra nếu tham số này = 1?

  2. anh ơi cho em hỏi về vấn đề nếu đưa ra ngoài đời để làm sản phẩm thì mình cần lưu ý đến các vấn đề nào nữa ạ, anh có thể chỉ em một số lưu ý không ạ

  3. Anh thắng ơi cho em hỏi, phần mình chia train_test_split 80-20, có phải 20% này là validatrion trong quá trình train không vậy anh?

  4. validArgumentError: logits and labels must have the same first dimension, got logits shape [64,5] and labels shape [320]

    anh ơi cho em hỏi, em dùng code nhận dạng tiền để train 1 bài toán 5 class, em train thì ra lỗi này ạ .Em train trên colab, nó báo lỗi ngay dòng này ạ:–> 119 callbacks=callbacks_list)

  5. Link tải file weights hình như lỗi rồi bạn. Bạn thử check lại xem, mình không tải được

  6. Chào anh, em thấy project rất hay. Anh cho em hỏi chút, project này có thể nhận diện tiền thật, tiền giả không ? Cám ơn chia sẻ của anh.

  7. Anh cho em hỏi là tại sao cái input của vgg16 mình lại cho ở kích thước 128×128 vậy ạ?
    input = Input(shape=(128, 128, 3), name=’image_input’)

  8. Traceback (most recent call last):
    File “C:\Users\MSI\PycharmProjects\MiAI_Money_Classify-master\train.py”, line 67, in
    X,y = load_data()
    File “C:\Users\MSI\PycharmProjects\MiAI_Money_Classify-master\train.py”, line 52, in load_data
    file = open(‘pix.data’, ‘rb’)
    FileNotFoundError: [Errno 2] No such file or directory: ‘pix.data’
    cho em hỏi phần của em tải về chạy nó hiện như này khi chạy train là sao ạ

Leave a Reply

Your email address will not be published. Required fields are marked *