├── .DS_Store ├── .gitattributes ├── Data ├── .DS_Store ├── capture_data.py └── create_data.py ├── README.md ├── cv_chess.py ├── cv_chess_functions.py └── cv_chess_model_and_eval.ipynb /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewleeunderwood/project_MYM/fe8da0b5755e13943570f23996e9fa5d92ad6541/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /Data/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrewleeunderwood/project_MYM/fe8da0b5755e13943570f23996e9fa5d92ad6541/Data/.DS_Store -------------------------------------------------------------------------------- /Data/capture_data.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | 4 | def rescale_frame(frame, percent=75): 5 | # width = int(frame.shape[1] * (percent / 100)) 6 | # height = int(frame.shape[0] * (percent / 100)) 7 | dim = (1000, 750) 8 | return cv2.resize(frame, dim, interpolation=cv2.INTER_AREA) 9 | 10 | 11 | cap = cv2.VideoCapture(1) 12 | 13 | capture_num = 0 14 | 15 | while(True): 16 | # Capture frame-by-frame 17 | ret, frame = cap.read() 18 | 19 | # Our operations on the frame come here 20 | # gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) 21 | small_frame = rescale_frame(frame) 22 | 23 | # Display the resulting frame 24 | cv2.imshow('frame', frame) 25 | if cv2.waitKey(1) & 0xFF == ord('s'): 26 | cv2.imwrite('frame' + str(capture_num) + '.jpeg', frame) 27 | print('Saved ' + str(capture_num)) 28 | capture_num += 1 29 | if cv2.waitKey(1) & 0xFF == ord('q'): 30 | break 31 | 32 | # When everything done, release the capture 33 | cap.release() 34 | cv2.destroyAllWindows() -------------------------------------------------------------------------------- /Data/create_data.py: -------------------------------------------------------------------------------- 1 | import glob 2 | # import re 3 | import math 4 | import cv2 5 | import numpy as np 6 | import scipy.spatial as spatial 7 | import scipy.cluster as cluster 8 | from collections import defaultdict 9 | from statistics import mean 10 | 11 | 12 | # Read image and do lite image processing 13 | def read_img(file): 14 | img = cv2.imread(str(file), 1) 15 | 16 | W = 1000 17 | height, width, depth = img.shape 18 | imgScale = W / width 19 | newX, newY = img.shape[1] * imgScale, img.shape[0] * imgScale 20 | img = cv2.resize(img, (int(newX), int(newY))) 21 | 22 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 23 | gray_blur = cv2.blur(gray, (5, 5)) 24 | return img, gray_blur 25 | 26 | 27 | # Canny edge detection 28 | def canny_edge(img, sigma=0.33): 29 | v = np.median(img) 30 | lower = int(max(0, (1.0 - sigma) * v)) 31 | upper = int(min(255, (1.0 + sigma) * v)) 32 | edges = cv2.Canny(img, lower, upper) 33 | return edges 34 | 35 | 36 | # Hough line detection 37 | def hough_line(edges, min_line_length=100, max_line_gap=10): 38 | lines = cv2.HoughLines(edges, 1, np.pi / 180, 125, min_line_length, max_line_gap) 39 | lines = np.reshape(lines, (-1, 2)) 40 | return lines 41 | 42 | 43 | # Separate line into horizontal and vertical 44 | def h_v_lines(lines): 45 | h_lines, v_lines = [], [] 46 | for rho, theta in lines: 47 | if theta < np.pi / 4 or theta > np.pi - np.pi / 4: 48 | v_lines.append([rho, theta]) 49 | else: 50 | h_lines.append([rho, theta]) 51 | return h_lines, v_lines 52 | 53 | 54 | # Find the intersections of the lines 55 | def line_intersections(h_lines, v_lines): 56 | points = [] 57 | for r_h, t_h in h_lines: 58 | for r_v, t_v in v_lines: 59 | a = np.array([[np.cos(t_h), np.sin(t_h)], [np.cos(t_v), np.sin(t_v)]]) 60 | b = np.array([r_h, r_v]) 61 | inter_point = np.linalg.solve(a, b) 62 | points.append(inter_point) 63 | return np.array(points) 64 | 65 | 66 | # Hierarchical cluster (by euclidean distance) intersection points 67 | def cluster_points(points): 68 | dists = spatial.distance.pdist(points) 69 | single_linkage = cluster.hierarchy.single(dists) 70 | flat_clusters = cluster.hierarchy.fcluster(single_linkage, 15, 'distance') 71 | cluster_dict = defaultdict(list) 72 | for i in range(len(flat_clusters)): 73 | cluster_dict[flat_clusters[i]].append(points[i]) 74 | cluster_values = cluster_dict.values() 75 | clusters = map(lambda arr: (np.mean(np.array(arr)[:, 0]), np.mean(np.array(arr)[:, 1])), cluster_values) 76 | return sorted(list(clusters), key=lambda k: [k[1], k[0]]) 77 | 78 | 79 | # Average the y value in each row and augment original point 80 | def augment_points(points): 81 | points_shape = list(np.shape(points)) 82 | augmented_points = [] 83 | for row in range(int(points_shape[0] / 11)): 84 | start = row * 11 85 | end = (row * 11) + 10 86 | rw_points = points[start:end + 1] 87 | rw_y = [] 88 | rw_x = [] 89 | for point in rw_points: 90 | x, y = point 91 | rw_y.append(y) 92 | rw_x.append(x) 93 | y_mean = mean(rw_y) 94 | for i in range(len(rw_x)): 95 | point = (rw_x[i], y_mean) 96 | augmented_points.append(point) 97 | augmented_points = sorted(augmented_points, key=lambda k: [k[1], k[0]]) 98 | return augmented_points 99 | 100 | 101 | # Crop board into separate images 102 | def write_crop_images(img, points, img_count, folder_path='./raw_data/'): 103 | num_list = [] 104 | shape = list(np.shape(points)) 105 | start_point = shape[0] - 14 106 | 107 | if int(shape[0] / 11) >= 8: 108 | range_num = 8 109 | else: 110 | range_num = int((shape[0] / 11) - 2) 111 | 112 | for row in range(range_num): 113 | start = start_point - (row * 11) 114 | end = (start_point - 8) - (row * 11) 115 | num_list.append(range(start, end, -1)) 116 | 117 | 118 | for row in num_list: 119 | for s in row: 120 | # ratio_h = 2 121 | # ratio_w = 1 122 | base_len = math.dist(points[s], points[s + 1]) 123 | bot_left, bot_right = points[s], points[s + 1] 124 | start_x, start_y = int(bot_left[0]), int(bot_left[1] - (base_len * 2)) 125 | end_x, end_y = int(bot_right[0]), int(bot_right[1]) 126 | if start_y < 0: 127 | start_y = 0 128 | cropped = img[start_y: end_y, start_x: end_x] 129 | img_count += 1 130 | cv2.imwrite('./raw_data/alpha_data_image' + str(img_count) + '.jpeg', cropped) 131 | # print(folder_path + 'data' + str(img_count) + '.jpeg') 132 | return img_count 133 | 134 | 135 | # Create a list of image file names 136 | img_filename_list = [] 137 | folder_name = './test_data/*' 138 | for path_name in glob.glob(folder_name): 139 | # file_name = re.search("[\w-]+\.\w+", path_name) (use if in same folder) 140 | img_filename_list.append(path_name) # file_name.group() 141 | 142 | # Create and save cropped images from original images to the data folder 143 | img_count = 20000 144 | print_number = 0 145 | for file_name in img_filename_list: 146 | print(file_name) 147 | img, gray_blur = read_img(file_name) 148 | print(np.shape(img)) 149 | print(np.shape(gray_blur)) 150 | edges = canny_edge(gray_blur) 151 | print('edges: ' + str(np.shape(edges))) 152 | lines = hough_line(edges) 153 | print('line: ' + str(np.shape(lines))) 154 | h_lines, v_lines = h_v_lines(lines) 155 | assert len(h_lines) >= 11 156 | assert len(v_lines) >= 11 157 | print('h_lines: ' + str(np.shape(h_lines))) 158 | print('v_lines: ' + str(np.shape(v_lines))) 159 | intersection_points = line_intersections(h_lines, v_lines) 160 | print('lines: ' + str(np.shape(intersection_points))) 161 | points = cluster_points(intersection_points) 162 | # if np.shape(points)[0] < 100: 163 | # continue 164 | points = augment_points(points) 165 | print('points: ' + str(np.shape(points))) 166 | img_count = write_crop_images(img, points, img_count) 167 | print('img_count: ' + str(img_count)) 168 | print('PRINTED') 169 | print_number += 1 170 | print(print_number) 171 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Make Your Move: Image Recognition using Neural Networks 2 | 3 | Combined computer vision techniques and convolutional neural networks to accurately classify chess pieces and identified their location on a chessboard. Tools: Python, Google Cloud, Keras, TensorFlow, OpenCV, Pillow, Scikit-learn, NumPy, Seaborn, and others 4 | 5 | Project Published on TowardDataScience.com and Data Science Weekly: 6 | https://towardsdatascience.com/board-game-image-recognition-using-neural-networks-116fc876dafa 7 | 8 | Original Dataset: 9 | https://www.dropbox.com/sh/8s4tvir5zbotseq/AACAQypmuFb6j-Yww9x9Q6Gta?dl=0 10 | -------------------------------------------------------------------------------- /cv_chess.py: -------------------------------------------------------------------------------- 1 | import re 2 | import cv2 3 | from keras.models import load_model 4 | from cvchess_functions import (read_img, 5 | canny_edge, 6 | hough_line, 7 | h_v_lines, 8 | line_intersections, 9 | cluster_points, 10 | augment_points, 11 | write_crop_images, 12 | grab_cell_files, 13 | classify_cells, 14 | fen_to_image, 15 | atoi) 16 | 17 | 18 | # Resize the frame by scale by dimensions 19 | def rescale_frame(frame, percent=75): 20 | # width = int(frame.shape[1] * (percent / 100)) 21 | # height = int(frame.shape[0] * (percent / 100)) 22 | dim = (1000, 750) 23 | return cv2.resize(frame, dim, interpolation=cv2.INTER_AREA) 24 | 25 | 26 | # Find the number(s) in the text 27 | def natural_keys(text): 28 | return [atoi(c) for c in re.split('(\d+)', text)] 29 | 30 | 31 | # Load in the CNN model 32 | model = load_model('model_VGG16.h5') 33 | 34 | # Select the live video stream source (0-webcam & 1-GoPro) 35 | cap = cv2.VideoCapture(1) 36 | 37 | # Show the starting board either as blank or with the initial setup 38 | # start = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR' 39 | blank = '8/8/8/8/8/8/8/8' 40 | board = fen_to_image(blank) 41 | board_image = cv2.imread('current_board.png') 42 | cv2.imshow('current board', board_image) 43 | 44 | while(True): 45 | # Capture frame-by-frame 46 | ret, frame = cap.read() 47 | 48 | # Resizes each frame 49 | small_frame = rescale_frame(frame) 50 | 51 | # Display the resulting frame 52 | cv2.imshow('live', small_frame) 53 | 54 | if cv2.waitKey(1) & 0xFF == ord(' '): 55 | 56 | print('Working...') 57 | # Save the frame to be analyzed 58 | cv2.imwrite('frame.jpeg', frame) 59 | # Low-level CV techniques (grayscale & blur) 60 | img, gray_blur = read_img('frame.jpeg') 61 | # Canny algorithm 62 | edges = canny_edge(gray_blur) 63 | # Hough Transform 64 | lines = hough_line(edges) 65 | # Separate the lines into vertical and horizontal lines 66 | h_lines, v_lines = h_v_lines(lines) 67 | # Find and cluster the intersecting 68 | intersection_points = line_intersections(h_lines, v_lines) 69 | points = cluster_points(intersection_points) 70 | # Final coordinates of the board 71 | points = augment_points(points) 72 | # Crop the squares of the board a organize into a sorted list 73 | x_list = write_crop_images(img, points, 0) 74 | img_filename_list = grab_cell_files() 75 | img_filename_list.sort(key=natural_keys) 76 | # Classify each square and output the board in Forsyth-Edwards Notation (FEN) 77 | fen = classify_cells(model, img_filename_list) 78 | # Create and save the board image from the FEN 79 | board = fen_to_image(fen) 80 | # Display the board in ASCII 81 | print(board) 82 | # Display and save the board image 83 | board_image = cv2.imread('current_board.png') 84 | cv2.imshow('current board', board_image) 85 | print('Completed!') 86 | 87 | if cv2.waitKey(1) & 0xFF == ord('q'): 88 | # End the program 89 | break 90 | 91 | # When everything is done, release the capture 92 | cap.release() 93 | cv2.destroyAllWindows() 94 | -------------------------------------------------------------------------------- /cv_chess_functions.py: -------------------------------------------------------------------------------- 1 | import math 2 | import cv2 3 | import numpy as np 4 | import scipy.spatial as spatial 5 | import scipy.cluster as cluster 6 | from collections import defaultdict 7 | from statistics import mean 8 | import chess 9 | import chess.svg 10 | from svglib.svglib import svg2rlg 11 | from reportlab.graphics import renderPM 12 | from PIL import Image 13 | import re 14 | import glob 15 | import PIL 16 | 17 | 18 | # Read image and do lite image processing 19 | def read_img(file): 20 | img = cv2.imread(str(file)) 21 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 22 | gray_blur = cv2.blur(gray, (5, 5)) 23 | return img, gray_blur 24 | 25 | 26 | # Canny edge detection 27 | def canny_edge(img, sigma=0.33): 28 | v = np.median(img) 29 | lower = int(max(0, (1.0 - sigma) * v)) 30 | upper = int(min(255, (1.0 + sigma) * v)) 31 | edges = cv2.Canny(img, lower, upper) 32 | return edges 33 | 34 | 35 | # Hough line detection 36 | def hough_line(edges, min_line_length=100, max_line_gap=10): 37 | lines = cv2.HoughLines(edges, 1, np.pi / 180, 125, min_line_length, max_line_gap) 38 | lines = np.reshape(lines, (-1, 2)) 39 | return lines 40 | 41 | 42 | # Separate line into horizontal and vertical 43 | def h_v_lines(lines): 44 | h_lines, v_lines = [], [] 45 | for rho, theta in lines: 46 | if theta < np.pi / 4 or theta > np.pi - np.pi / 4: 47 | v_lines.append([rho, theta]) 48 | else: 49 | h_lines.append([rho, theta]) 50 | return h_lines, v_lines 51 | 52 | 53 | # Find the intersections of the lines 54 | def line_intersections(h_lines, v_lines): 55 | points = [] 56 | for r_h, t_h in h_lines: 57 | for r_v, t_v in v_lines: 58 | a = np.array([[np.cos(t_h), np.sin(t_h)], [np.cos(t_v), np.sin(t_v)]]) 59 | b = np.array([r_h, r_v]) 60 | inter_point = np.linalg.solve(a, b) 61 | points.append(inter_point) 62 | return np.array(points) 63 | 64 | 65 | # Hierarchical cluster (by euclidean distance) intersection points 66 | def cluster_points(points): 67 | dists = spatial.distance.pdist(points) 68 | single_linkage = cluster.hierarchy.single(dists) 69 | flat_clusters = cluster.hierarchy.fcluster(single_linkage, 15, 'distance') 70 | cluster_dict = defaultdict(list) 71 | for i in range(len(flat_clusters)): 72 | cluster_dict[flat_clusters[i]].append(points[i]) 73 | cluster_values = cluster_dict.values() 74 | clusters = map(lambda arr: (np.mean(np.array(arr)[:, 0]), np.mean(np.array(arr)[:, 1])), cluster_values) 75 | return sorted(list(clusters), key=lambda k: [k[1], k[0]]) 76 | 77 | 78 | # Average the y value in each row and augment original points 79 | def augment_points(points): 80 | points_shape = list(np.shape(points)) 81 | augmented_points = [] 82 | for row in range(int(points_shape[0] / 11)): 83 | start = row * 11 84 | end = (row * 11) + 10 85 | rw_points = points[start:end + 1] 86 | rw_y = [] 87 | rw_x = [] 88 | for point in rw_points: 89 | x, y = point 90 | rw_y.append(y) 91 | rw_x.append(x) 92 | y_mean = mean(rw_y) 93 | for i in range(len(rw_x)): 94 | point = (rw_x[i], y_mean) 95 | augmented_points.append(point) 96 | augmented_points = sorted(augmented_points, key=lambda k: [k[1], k[0]]) 97 | return augmented_points 98 | 99 | 100 | # Crop board into separate images and write to folder 101 | def write_crop_images(img, points, img_count=0, folder_path='./Data/raw_data/'): 102 | num_list = [] 103 | shape = list(np.shape(points)) 104 | start_point = shape[0] - 14 105 | 106 | if int(shape[0] / 11) >= 8: 107 | range_num = 8 108 | else: 109 | range_num = int((shape[0] / 11) - 2) 110 | 111 | for row in range(range_num): 112 | start = start_point - (row * 11) 113 | end = (start_point - 8) - (row * 11) 114 | num_list.append(range(start, end, -1)) 115 | 116 | for row in num_list: 117 | for s in row: 118 | # ratio_h = 2 119 | # ratio_w = 1 120 | base_len = math.dist(points[s], points[s + 1]) 121 | bot_left, bot_right = points[s], points[s + 1] 122 | start_x, start_y = int(bot_left[0]), int(bot_left[1] - (base_len * 2)) 123 | end_x, end_y = int(bot_right[0]), int(bot_right[1]) 124 | if start_y < 0: 125 | start_y = 0 126 | cropped = img[start_y: end_y, start_x: end_x] 127 | img_count += 1 128 | cv2.imwrite('./Data/raw_data/data_image' + str(img_count) + '.jpeg', cropped) 129 | # print(folder_path + 'data' + str(img_count) + '.jpeg') 130 | return img_count 131 | 132 | 133 | # Crop board into separate images and shows 134 | def x_crop_images(img, points): 135 | num_list = [] 136 | img_list = [] 137 | shape = list(np.shape(points)) 138 | start_point = shape[0] - 14 139 | 140 | if int(shape[0] / 11) >= 8: 141 | range_num = 8 142 | else: 143 | range_num = int((shape[0] / 11) - 2) 144 | 145 | for row in range(range_num): 146 | start = start_point - (row * 11) 147 | end = (start_point - 8) - (row * 11) 148 | num_list.append(range(start, end, -1)) 149 | 150 | for row in num_list: 151 | for s in row: 152 | base_len = math.dist(points[s], points[s + 1]) 153 | bot_left, bot_right = points[s], points[s + 1] 154 | start_x, start_y = int(bot_left[0]), int(bot_left[1] - (base_len * 2)) 155 | end_x, end_y = int(bot_right[0]), int(bot_right[1]) 156 | if start_y < 0: 157 | start_y = 0 158 | cropped = img[start_y: end_y, start_x: end_x] 159 | img_list.append(cropped) 160 | # print(folder_path + 'data' + str(img_count) + '.jpeg') 161 | return img_list 162 | 163 | 164 | # Convert image from RGB to BGR 165 | def convert_image_to_bgr_numpy_array(image_path, size=(224, 224)): 166 | image = PIL.Image.open(image_path).resize(size) 167 | img_data = np.array(image.getdata(), np.float32).reshape(*size, -1) 168 | # swap R and B channels 169 | img_data = np.flip(img_data, axis=2) 170 | return img_data 171 | 172 | 173 | # Adjust image into (1, 224, 224, 3) 174 | def prepare_image(image_path): 175 | im = convert_image_to_bgr_numpy_array(image_path) 176 | 177 | im[:, :, 0] -= 103.939 178 | im[:, :, 1] -= 116.779 179 | im[:, :, 2] -= 123.68 180 | 181 | im = np.expand_dims(im, axis=0) 182 | return im 183 | 184 | 185 | # Changes digits in text to ints 186 | def atoi(text): 187 | return int(text) if text.isdigit() else text 188 | 189 | 190 | # Finds the digits in a string 191 | def natural_keys(text): 192 | return [atoi(c) for c in re.split('(\d+)', text)] 193 | 194 | 195 | # Reads in the cropped images to a list 196 | def grab_cell_files(folder_name='./Data/raw_data/*'): 197 | img_filename_list = [] 198 | for path_name in glob.glob(folder_name): 199 | img_filename_list.append(path_name) 200 | # img_filename_list = img_filename_list.sort(key=natural_keys) 201 | return img_filename_list 202 | 203 | 204 | # Classifies each square and outputs the list in Forsyth-Edwards Notation (FEN) 205 | def classify_cells(model, img_filename_list): 206 | category_reference = {0: 'b', 1: 'k', 2: 'n', 3: 'p', 4: 'q', 5: 'r', 6: '1', 7: 'B', 8: 'K', 9: 'N', 10: 'P', 207 | 11: 'Q', 12: 'R'} 208 | pred_list = [] 209 | for filename in img_filename_list: 210 | img = prepare_image(filename) 211 | out = model.predict(img) 212 | top_pred = np.argmax(out) 213 | pred = category_reference[top_pred] 214 | pred_list.append(pred) 215 | 216 | fen = ''.join(pred_list) 217 | fen = fen[::-1] 218 | fen = '/'.join(fen[i:i + 8] for i in range(0, len(fen), 8)) 219 | sum_digits = 0 220 | for i, p in enumerate(fen): 221 | if p.isdigit(): 222 | sum_digits += 1 223 | elif p.isdigit() is False and (fen[i - 1].isdigit() or i == len(fen)): 224 | fen = fen[:(i - sum_digits)] + str(sum_digits) + ('D' * (sum_digits - 1)) + fen[i:] 225 | sum_digits = 0 226 | if sum_digits > 1: 227 | fen = fen[:(len(fen) - sum_digits)] + str(sum_digits) + ('D' * (sum_digits - 1)) 228 | fen = fen.replace('D', '') 229 | return fen 230 | 231 | 232 | # Converts the FEN into a PNG file 233 | def fen_to_image(fen): 234 | board = chess.Board(fen) 235 | current_board = chess.svg.board(board=board) 236 | 237 | output_file = open('current_board.svg', "w") 238 | output_file.write(current_board) 239 | output_file.close() 240 | 241 | svg = svg2rlg('current_board.svg') 242 | renderPM.drawToFile(svg, 'current_board.png', fmt="PNG") 243 | return board 244 | -------------------------------------------------------------------------------- /cv_chess_model_and_eval.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "name": "cv_chess_model_and_eval.ipynb", 7 | "provenance": [] 8 | }, 9 | "kernelspec": { 10 | "name": "python3", 11 | "display_name": "Python 3" 12 | }, 13 | "accelerator": "GPU" 14 | }, 15 | "cells": [ 16 | { 17 | "cell_type": "code", 18 | "metadata": { 19 | "id": "FugnPep-wemo", 20 | "colab_type": "code", 21 | "colab": { 22 | "base_uri": "https://localhost:8080/", 23 | "height": 34 24 | }, 25 | "outputId": "8ab2398e-e7f3-4aec-afe4-fbc9f7bbbd15" 26 | }, 27 | "source": [ 28 | "from google.colab import drive\n", 29 | "drive.mount('/content/drive')" 30 | ], 31 | "execution_count": 1, 32 | "outputs": [ 33 | { 34 | "output_type": "stream", 35 | "text": [ 36 | "Mounted at /content/drive\n" 37 | ], 38 | "name": "stdout" 39 | } 40 | ] 41 | }, 42 | { 43 | "cell_type": "code", 44 | "metadata": { 45 | "id": "8ldZe6OlwiuP", 46 | "colab_type": "code", 47 | "colab": {} 48 | }, 49 | "source": [ 50 | "import numpy as np\n", 51 | "import tensorflow as tf\n", 52 | "from tensorflow import keras\n", 53 | "from tensorflow.keras import layers" 54 | ], 55 | "execution_count": 2, 56 | "outputs": [] 57 | }, 58 | { 59 | "cell_type": "code", 60 | "metadata": { 61 | "id": "Z-NS0l7Jwyb2", 62 | "colab_type": "code", 63 | "colab": {} 64 | }, 65 | "source": [ 66 | "folder = '/content/drive/My Drive/Colab Data/public_data'\n", 67 | "image_size = (224, 224)\n", 68 | "batch_size = 32" 69 | ], 70 | "execution_count": 3, 71 | "outputs": [] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "metadata": { 76 | "id": "-OeAL7oFyWnO", 77 | "colab_type": "code", 78 | "colab": {} 79 | }, 80 | "source": [ 81 | "from keras.preprocessing.image import ImageDataGenerator\n", 82 | "\n", 83 | "datagen = ImageDataGenerator(\n", 84 | " rotation_range=5,\n", 85 | " # width_shift_range=0.1,\n", 86 | " # height_shift_range=0.1,\n", 87 | " rescale=1./255,\n", 88 | " horizontal_flip=True,\n", 89 | " fill_mode='nearest')\n", 90 | "\n", 91 | "test_datagen = ImageDataGenerator(rescale=1./255)" 92 | ], 93 | "execution_count": 4, 94 | "outputs": [] 95 | }, 96 | { 97 | "cell_type": "code", 98 | "metadata": { 99 | "id": "5FzaqjxD0cwd", 100 | "colab_type": "code", 101 | "colab": { 102 | "base_uri": "https://localhost:8080/", 103 | "height": 51 104 | }, 105 | "outputId": "1e647b4c-c0fb-4e4f-ea55-157075190182" 106 | }, 107 | "source": [ 108 | "train_gen = datagen.flow_from_directory(\n", 109 | " folder + '/train',\n", 110 | " target_size = image_size,\n", 111 | " batch_size = batch_size,\n", 112 | " class_mode = 'categorical',\n", 113 | " color_mode = 'rgb',\n", 114 | " shuffle=True \n", 115 | ")\n", 116 | "\n", 117 | "test_gen = test_datagen.flow_from_directory(\n", 118 | " folder + '/test',\n", 119 | " target_size = image_size,\n", 120 | " batch_size = batch_size,\n", 121 | " class_mode = 'categorical',\n", 122 | " color_mode = 'rgb',\n", 123 | " shuffle=False \n", 124 | ")" 125 | ], 126 | "execution_count": 5, 127 | "outputs": [ 128 | { 129 | "output_type": "stream", 130 | "text": [ 131 | "Found 1605 images belonging to 13 classes.\n", 132 | "Found 800 images belonging to 13 classes.\n" 133 | ], 134 | "name": "stdout" 135 | } 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "metadata": { 141 | "id": "Plq6_55C1MPc", 142 | "colab_type": "code", 143 | "colab": { 144 | "base_uri": "https://localhost:8080/", 145 | "height": 969 146 | }, 147 | "outputId": "b943a7e2-a63c-416c-dcad-93e89d5c6850" 148 | }, 149 | "source": [ 150 | "from keras.applications.vgg16 import VGG16\n", 151 | "from keras.applications.imagenet_utils import decode_predictions\n", 152 | "\n", 153 | "model = VGG16(weights='imagenet')\n", 154 | "model.summary()" 155 | ], 156 | "execution_count": 6, 157 | "outputs": [ 158 | { 159 | "output_type": "stream", 160 | "text": [ 161 | "Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels.h5\n", 162 | "553467904/553467096 [==============================] - 4s 0us/step\n", 163 | "Model: \"vgg16\"\n", 164 | "_________________________________________________________________\n", 165 | "Layer (type) Output Shape Param # \n", 166 | "=================================================================\n", 167 | "input_1 (InputLayer) [(None, 224, 224, 3)] 0 \n", 168 | "_________________________________________________________________\n", 169 | "block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 \n", 170 | "_________________________________________________________________\n", 171 | "block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 \n", 172 | "_________________________________________________________________\n", 173 | "block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 \n", 174 | "_________________________________________________________________\n", 175 | "block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 \n", 176 | "_________________________________________________________________\n", 177 | "block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 \n", 178 | "_________________________________________________________________\n", 179 | "block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 \n", 180 | "_________________________________________________________________\n", 181 | "block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 \n", 182 | "_________________________________________________________________\n", 183 | "block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 \n", 184 | "_________________________________________________________________\n", 185 | "block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 \n", 186 | "_________________________________________________________________\n", 187 | "block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 \n", 188 | "_________________________________________________________________\n", 189 | "block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 \n", 190 | "_________________________________________________________________\n", 191 | "block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 \n", 192 | "_________________________________________________________________\n", 193 | "block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 \n", 194 | "_________________________________________________________________\n", 195 | "block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 \n", 196 | "_________________________________________________________________\n", 197 | "block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 \n", 198 | "_________________________________________________________________\n", 199 | "block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 \n", 200 | "_________________________________________________________________\n", 201 | "block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 \n", 202 | "_________________________________________________________________\n", 203 | "block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 \n", 204 | "_________________________________________________________________\n", 205 | "flatten (Flatten) (None, 25088) 0 \n", 206 | "_________________________________________________________________\n", 207 | "fc1 (Dense) (None, 4096) 102764544 \n", 208 | "_________________________________________________________________\n", 209 | "fc2 (Dense) (None, 4096) 16781312 \n", 210 | "_________________________________________________________________\n", 211 | "predictions (Dense) (None, 1000) 4097000 \n", 212 | "=================================================================\n", 213 | "Total params: 138,357,544\n", 214 | "Trainable params: 138,357,544\n", 215 | "Non-trainable params: 0\n", 216 | "_________________________________________________________________\n" 217 | ], 218 | "name": "stdout" 219 | } 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "metadata": { 225 | "id": "7J1btS1W3JBP", 226 | "colab_type": "code", 227 | "colab": { 228 | "base_uri": "https://localhost:8080/", 229 | "height": 51 230 | }, 231 | "outputId": "8fcb326b-acc0-46d8-ecbe-dd45978f1aa0" 232 | }, 233 | "source": [ 234 | "from keras.models import Sequential\n", 235 | "from keras.layers import Dense, Conv2D, MaxPooling2D, Flatten\n", 236 | "from keras.models import Model\n", 237 | "\n", 238 | "base_model = VGG16(weights='imagenet', include_top=False, input_shape=(224,224,3)) \n", 239 | " \n", 240 | "# Freeze convolutional layers\n", 241 | "for layer in base_model.layers:\n", 242 | " layer.trainable = False \n", 243 | "\n", 244 | "# Establish new fully connected block\n", 245 | "x = base_model.output\n", 246 | "x = Flatten()(x) # flatten from convolution tensor output \n", 247 | "x = Dense(500, activation='relu')(x) # number of layers and units are hyperparameters, as usual\n", 248 | "x = Dense(500, activation='relu')(x)\n", 249 | "predictions = Dense(13, activation='softmax')(x) # should match # of classes predicted\n", 250 | "\n", 251 | "# this is the model we will train\n", 252 | "model = Model(inputs=base_model.input, outputs=predictions)\n", 253 | "model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['categorical_accuracy'])" 254 | ], 255 | "execution_count": 7, 256 | "outputs": [ 257 | { 258 | "output_type": "stream", 259 | "text": [ 260 | "Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels_notop.h5\n", 261 | "58892288/58889256 [==============================] - 0s 0us/step\n" 262 | ], 263 | "name": "stdout" 264 | } 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "metadata": { 270 | "id": "NWwMLiUu3Q34", 271 | "colab_type": "code", 272 | "colab": { 273 | "base_uri": "https://localhost:8080/", 274 | "height": 377 275 | }, 276 | "outputId": "d50fce4d-5ccb-4cbf-e5ba-da12835bbc48" 277 | }, 278 | "source": [ 279 | "epochs = 10\n", 280 | "\n", 281 | "history = model.fit(\n", 282 | " train_gen, \n", 283 | " epochs=epochs,\n", 284 | " verbose = 1,\n", 285 | " validation_data=test_gen\n", 286 | " )\n", 287 | "model.save_weights('model_VGG16.h5') " 288 | ], 289 | "execution_count": 8, 290 | "outputs": [ 291 | { 292 | "output_type": "stream", 293 | "text": [ 294 | "Epoch 1/10\n", 295 | "51/51 [==============================] - 673s 13s/step - loss: 2.6884 - categorical_accuracy: 0.2673 - val_loss: 1.4787 - val_categorical_accuracy: 0.5450\n", 296 | "Epoch 2/10\n", 297 | "51/51 [==============================] - 22s 433ms/step - loss: 1.1020 - categorical_accuracy: 0.6249 - val_loss: 0.8362 - val_categorical_accuracy: 0.7050\n", 298 | "Epoch 3/10\n", 299 | "51/51 [==============================] - 22s 433ms/step - loss: 0.6995 - categorical_accuracy: 0.7601 - val_loss: 0.7872 - val_categorical_accuracy: 0.7237\n", 300 | "Epoch 4/10\n", 301 | "51/51 [==============================] - 22s 430ms/step - loss: 0.4342 - categorical_accuracy: 0.8579 - val_loss: 0.5480 - val_categorical_accuracy: 0.8163\n", 302 | "Epoch 5/10\n", 303 | "51/51 [==============================] - 22s 430ms/step - loss: 0.2611 - categorical_accuracy: 0.9159 - val_loss: 0.5692 - val_categorical_accuracy: 0.8037\n", 304 | "Epoch 6/10\n", 305 | "51/51 [==============================] - 22s 430ms/step - loss: 0.1994 - categorical_accuracy: 0.9346 - val_loss: 0.4509 - val_categorical_accuracy: 0.8537\n", 306 | "Epoch 7/10\n", 307 | "51/51 [==============================] - 22s 429ms/step - loss: 0.2150 - categorical_accuracy: 0.9296 - val_loss: 0.5307 - val_categorical_accuracy: 0.8325\n", 308 | "Epoch 8/10\n", 309 | "51/51 [==============================] - 22s 430ms/step - loss: 0.1685 - categorical_accuracy: 0.9489 - val_loss: 0.6698 - val_categorical_accuracy: 0.7825\n", 310 | "Epoch 9/10\n", 311 | "51/51 [==============================] - 22s 430ms/step - loss: 0.1472 - categorical_accuracy: 0.9458 - val_loss: 0.4455 - val_categorical_accuracy: 0.8675\n", 312 | "Epoch 10/10\n", 313 | "51/51 [==============================] - 22s 428ms/step - loss: 0.0988 - categorical_accuracy: 0.9701 - val_loss: 0.4281 - val_categorical_accuracy: 0.8850\n" 314 | ], 315 | "name": "stdout" 316 | } 317 | ] 318 | }, 319 | { 320 | "cell_type": "code", 321 | "metadata": { 322 | "id": "w2oNGGKeLo5n", 323 | "colab_type": "code", 324 | "colab": { 325 | "base_uri": "https://localhost:8080/", 326 | "height": 349 327 | }, 328 | "outputId": "11cfc189-0a0a-4c48-c3ba-74b77dce29e4" 329 | }, 330 | "source": [ 331 | "import seaborn as sn\n", 332 | "import matplotlib.pyplot as plt\n", 333 | "import pandas as pd\n", 334 | "\n", 335 | "plt.plot(history.history['categorical_accuracy'], 'ko')\n", 336 | "plt.plot(history.history['val_categorical_accuracy'], 'b')\n", 337 | "\n", 338 | "plt.title('Accuracy vs Training Epoch')\n", 339 | "plt.xlabel('Epoch')\n", 340 | "plt.ylabel('Accuracy')\n", 341 | "plt.legend(['Train', 'Validation']);" 342 | ], 343 | "execution_count": 9, 344 | "outputs": [ 345 | { 346 | "output_type": "stream", 347 | "text": [ 348 | "/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.\n", 349 | " import pandas.util.testing as tm\n" 350 | ], 351 | "name": "stderr" 352 | }, 353 | { 354 | "output_type": "display_data", 355 | "data": { 356 | "image/png": "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\n", 357 | "text/plain": [ 358 | "
" 359 | ] 360 | }, 361 | "metadata": { 362 | "tags": [], 363 | "needs_background": "light" 364 | } 365 | } 366 | ] 367 | }, 368 | { 369 | "cell_type": "code", 370 | "metadata": { 371 | "id": "3P7FHX05MLUD", 372 | "colab_type": "code", 373 | "colab": { 374 | "base_uri": "https://localhost:8080/", 375 | "height": 943 376 | }, 377 | "outputId": "0c7987d1-75fb-4449-a8e6-5f5e07514289" 378 | }, 379 | "source": [ 380 | "from sklearn.metrics import classification_report, confusion_matrix\n", 381 | "\n", 382 | "target_names = ['BB', 'BK', 'BN', 'BP', 'BQ', 'BR', 'Empty', 'WB', 'WK', 'WN', 'WP', 'WQ', 'WR']\n", 383 | "\n", 384 | "test_gen.reset()\n", 385 | "Y_pred = model.predict_generator(test_gen)\n", 386 | "classes = test_gen.classes[test_gen.index_array]\n", 387 | "y_pred = np.argmax(Y_pred, axis= -1)\n", 388 | "print(sum(y_pred==classes)/800)\n", 389 | "\n", 390 | "\n", 391 | "data = confusion_matrix(classes, y_pred)\n", 392 | "df_cm = pd.DataFrame(data, columns=target_names, index = target_names)\n", 393 | "df_cm.index.name = 'Actual'\n", 394 | "df_cm.columns.name = 'Predicted'\n", 395 | "plt.figure(figsize = (20,14))\n", 396 | "sn.set(font_scale=1.4)#for label size\n", 397 | "sn.heatmap(df_cm, cmap=\"Blues\", annot=True,annot_kws={\"size\": 16})# font size" 398 | ], 399 | "execution_count": 10, 400 | "outputs": [ 401 | { 402 | "output_type": "stream", 403 | "text": [ 404 | "WARNING:tensorflow:From :6: Model.predict_generator (from tensorflow.python.keras.engine.training) is deprecated and will be removed in a future version.\n", 405 | "Instructions for updating:\n", 406 | "Please use Model.predict, which supports generators.\n", 407 | "0.885\n" 408 | ], 409 | "name": "stdout" 410 | }, 411 | { 412 | "output_type": "execute_result", 413 | "data": { 414 | "text/plain": [ 415 | "" 416 | ] 417 | }, 418 | "metadata": { 419 | "tags": [] 420 | }, 421 | "execution_count": 10 422 | }, 423 | { 424 | "output_type": "display_data", 425 | "data": { 426 | "image/png": "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\n", 427 | "text/plain": [ 428 | "
" 429 | ] 430 | }, 431 | "metadata": { 432 | "tags": [], 433 | "needs_background": "light" 434 | } 435 | } 436 | ] 437 | }, 438 | { 439 | "cell_type": "code", 440 | "metadata": { 441 | "id": "RwuWZwvy4VcS", 442 | "colab_type": "code", 443 | "colab": { 444 | "base_uri": "https://localhost:8080/", 445 | "height": 612 446 | }, 447 | "outputId": "28084712-1526-44c5-cd52-0f74b0d4dc84" 448 | }, 449 | "source": [ 450 | "print('Confusion Matrix')\n", 451 | "print(data)\n", 452 | "print('Classification Report')\n", 453 | "print(classification_report(test_gen.classes[test_gen.index_array], y_pred, target_names=target_names))" 454 | ], 455 | "execution_count": 11, 456 | "outputs": [ 457 | { 458 | "output_type": "stream", 459 | "text": [ 460 | "Confusion Matrix\n", 461 | "[[60 0 0 1 3 2 0 0 0 0 0 0 0]\n", 462 | " [ 1 27 0 0 5 1 0 0 0 0 0 0 0]\n", 463 | " [ 2 0 46 0 3 6 0 0 0 0 0 0 0]\n", 464 | " [ 2 0 0 60 0 1 1 0 0 0 0 0 0]\n", 465 | " [ 0 2 1 0 59 0 0 0 0 0 0 0 0]\n", 466 | " [ 1 0 1 0 3 60 0 0 0 0 0 0 0]\n", 467 | " [ 0 0 0 0 0 0 71 0 0 0 0 0 0]\n", 468 | " [ 0 0 0 0 0 0 0 53 0 13 1 1 4]\n", 469 | " [ 0 0 0 0 0 0 0 3 29 1 0 4 0]\n", 470 | " [ 0 0 0 0 0 0 0 0 0 64 1 0 0]\n", 471 | " [ 0 0 0 0 0 0 0 0 0 4 64 0 1]\n", 472 | " [ 0 0 0 0 0 0 0 1 4 3 0 60 0]\n", 473 | " [ 0 0 0 0 0 0 0 0 1 14 0 0 55]]\n", 474 | "Classification Report\n", 475 | " precision recall f1-score support\n", 476 | "\n", 477 | " BB 0.91 0.91 0.91 66\n", 478 | " BK 0.93 0.79 0.86 34\n", 479 | " BN 0.96 0.81 0.88 57\n", 480 | " BP 0.98 0.94 0.96 64\n", 481 | " BQ 0.81 0.95 0.87 62\n", 482 | " BR 0.86 0.92 0.89 65\n", 483 | " Empty 0.99 1.00 0.99 71\n", 484 | " WB 0.93 0.74 0.82 72\n", 485 | " WK 0.85 0.78 0.82 37\n", 486 | " WN 0.65 0.98 0.78 65\n", 487 | " WP 0.97 0.93 0.95 69\n", 488 | " WQ 0.92 0.88 0.90 68\n", 489 | " WR 0.92 0.79 0.85 70\n", 490 | "\n", 491 | " accuracy 0.89 800\n", 492 | " macro avg 0.90 0.88 0.88 800\n", 493 | "weighted avg 0.90 0.89 0.89 800\n", 494 | "\n" 495 | ], 496 | "name": "stdout" 497 | } 498 | ] 499 | }, 500 | { 501 | "cell_type": "code", 502 | "metadata": { 503 | "id": "u3bYZ_36oVv-", 504 | "colab_type": "code", 505 | "colab": { 506 | "base_uri": "https://localhost:8080/", 507 | "height": 1000 508 | }, 509 | "outputId": "695ad3bf-8d90-41fe-b6eb-481488eb5380" 510 | }, 511 | "source": [ 512 | "from keras.applications.vgg19 import VGG19\n", 513 | "from keras.applications.imagenet_utils import decode_predictions\n", 514 | "\n", 515 | "model_two = VGG19(weights='imagenet')\n", 516 | "model_two.summary()" 517 | ], 518 | "execution_count": 12, 519 | "outputs": [ 520 | { 521 | "output_type": "stream", 522 | "text": [ 523 | "Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg19/vgg19_weights_tf_dim_ordering_tf_kernels.h5\n", 524 | "574717952/574710816 [==============================] - 3s 0us/step\n", 525 | "Model: \"vgg19\"\n", 526 | "_________________________________________________________________\n", 527 | "Layer (type) Output Shape Param # \n", 528 | "=================================================================\n", 529 | "input_3 (InputLayer) [(None, 224, 224, 3)] 0 \n", 530 | "_________________________________________________________________\n", 531 | "block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 \n", 532 | "_________________________________________________________________\n", 533 | "block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 \n", 534 | "_________________________________________________________________\n", 535 | "block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 \n", 536 | "_________________________________________________________________\n", 537 | "block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 \n", 538 | "_________________________________________________________________\n", 539 | "block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 \n", 540 | "_________________________________________________________________\n", 541 | "block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 \n", 542 | "_________________________________________________________________\n", 543 | "block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 \n", 544 | "_________________________________________________________________\n", 545 | "block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 \n", 546 | "_________________________________________________________________\n", 547 | "block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 \n", 548 | "_________________________________________________________________\n", 549 | "block3_conv4 (Conv2D) (None, 56, 56, 256) 590080 \n", 550 | "_________________________________________________________________\n", 551 | "block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 \n", 552 | "_________________________________________________________________\n", 553 | "block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 \n", 554 | "_________________________________________________________________\n", 555 | "block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 \n", 556 | "_________________________________________________________________\n", 557 | "block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 \n", 558 | "_________________________________________________________________\n", 559 | "block4_conv4 (Conv2D) (None, 28, 28, 512) 2359808 \n", 560 | "_________________________________________________________________\n", 561 | "block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 \n", 562 | "_________________________________________________________________\n", 563 | "block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 \n", 564 | "_________________________________________________________________\n", 565 | "block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 \n", 566 | "_________________________________________________________________\n", 567 | "block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 \n", 568 | "_________________________________________________________________\n", 569 | "block5_conv4 (Conv2D) (None, 14, 14, 512) 2359808 \n", 570 | "_________________________________________________________________\n", 571 | "block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 \n", 572 | "_________________________________________________________________\n", 573 | "flatten (Flatten) (None, 25088) 0 \n", 574 | "_________________________________________________________________\n", 575 | "fc1 (Dense) (None, 4096) 102764544 \n", 576 | "_________________________________________________________________\n", 577 | "fc2 (Dense) (None, 4096) 16781312 \n", 578 | "_________________________________________________________________\n", 579 | "predictions (Dense) (None, 1000) 4097000 \n", 580 | "=================================================================\n", 581 | "Total params: 143,667,240\n", 582 | "Trainable params: 143,667,240\n", 583 | "Non-trainable params: 0\n", 584 | "_________________________________________________________________\n" 585 | ], 586 | "name": "stdout" 587 | } 588 | ] 589 | }, 590 | { 591 | "cell_type": "code", 592 | "metadata": { 593 | "id": "wopOBbpwvftf", 594 | "colab_type": "code", 595 | "colab": { 596 | "base_uri": "https://localhost:8080/", 597 | "height": 51 598 | }, 599 | "outputId": "d6022a56-f83c-4e00-b61c-07c50c13a7fc" 600 | }, 601 | "source": [ 602 | "from keras.models import Sequential\n", 603 | "from keras.layers import Dense, Conv2D, MaxPooling2D, Flatten\n", 604 | "from keras.models import Model\n", 605 | "\n", 606 | "base_model_two = VGG19(weights='imagenet', include_top=False, input_shape=(224,224,3)) \n", 607 | " \n", 608 | "# Freeze convolutional layers\n", 609 | "for layer in base_model_two.layers:\n", 610 | " layer.trainable = False \n", 611 | "\n", 612 | "# Establish new fully connected block\n", 613 | "x = base_model_two.output\n", 614 | "x = Flatten()(x) # flatten from convolution tensor output \n", 615 | "x = Dense(500, activation='relu')(x) # number of layers and units are hyperparameters, as usual\n", 616 | "x = Dense(500, activation='relu')(x)\n", 617 | "predictions = Dense(13, activation='softmax')(x) # should match # of classes predicted\n", 618 | "\n", 619 | "# this is the model we will train\n", 620 | "model_two = Model(inputs=base_model_two.input, outputs=predictions)\n", 621 | "model_two.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])" 622 | ], 623 | "execution_count": 13, 624 | "outputs": [ 625 | { 626 | "output_type": "stream", 627 | "text": [ 628 | "Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg19/vgg19_weights_tf_dim_ordering_tf_kernels_notop.h5\n", 629 | "80142336/80134624 [==============================] - 0s 0us/step\n" 630 | ], 631 | "name": "stdout" 632 | } 633 | ] 634 | }, 635 | { 636 | "cell_type": "code", 637 | "metadata": { 638 | "id": "X0nH7g1Zv3c_", 639 | "colab_type": "code", 640 | "colab": { 641 | "base_uri": "https://localhost:8080/", 642 | "height": 357 643 | }, 644 | "outputId": "6332c1db-a56d-4e60-92fd-391919a60348" 645 | }, 646 | "source": [ 647 | "epochs = 10\n", 648 | "\n", 649 | "history = model_two.fit(\n", 650 | " train_gen, \n", 651 | " epochs=epochs,\n", 652 | " verbose = 1,\n", 653 | " validation_data=test_gen\n", 654 | " )\n", 655 | "model.save_weights('model_VGG19.h5') " 656 | ], 657 | "execution_count": 14, 658 | "outputs": [ 659 | { 660 | "output_type": "stream", 661 | "text": [ 662 | "Epoch 1/10\n", 663 | "51/51 [==============================] - 23s 454ms/step - loss: 2.7242 - accuracy: 0.2380 - val_loss: 1.8447 - val_accuracy: 0.3500\n", 664 | "Epoch 2/10\n", 665 | "51/51 [==============================] - 23s 451ms/step - loss: 1.3450 - accuracy: 0.5464 - val_loss: 1.0877 - val_accuracy: 0.6275\n", 666 | "Epoch 3/10\n", 667 | "51/51 [==============================] - 23s 447ms/step - loss: 0.8556 - accuracy: 0.7065 - val_loss: 0.8117 - val_accuracy: 0.7138\n", 668 | "Epoch 4/10\n", 669 | "51/51 [==============================] - 23s 450ms/step - loss: 0.7130 - accuracy: 0.7514 - val_loss: 0.8722 - val_accuracy: 0.6737\n", 670 | "Epoch 5/10\n", 671 | "51/51 [==============================] - 23s 452ms/step - loss: 0.5393 - accuracy: 0.8150 - val_loss: 0.6614 - val_accuracy: 0.7600\n", 672 | "Epoch 6/10\n", 673 | "51/51 [==============================] - 23s 455ms/step - loss: 0.4539 - accuracy: 0.8442 - val_loss: 0.7770 - val_accuracy: 0.7375\n", 674 | "Epoch 7/10\n", 675 | "51/51 [==============================] - 23s 447ms/step - loss: 0.3024 - accuracy: 0.8978 - val_loss: 0.6327 - val_accuracy: 0.7875\n", 676 | "Epoch 8/10\n", 677 | "51/51 [==============================] - 23s 450ms/step - loss: 0.4077 - accuracy: 0.8673 - val_loss: 0.6253 - val_accuracy: 0.7862\n", 678 | "Epoch 9/10\n", 679 | "51/51 [==============================] - 23s 448ms/step - loss: 0.3182 - accuracy: 0.8847 - val_loss: 0.5462 - val_accuracy: 0.8200\n", 680 | "Epoch 10/10\n", 681 | "51/51 [==============================] - 23s 448ms/step - loss: 0.2018 - accuracy: 0.9389 - val_loss: 0.6776 - val_accuracy: 0.7725\n" 682 | ], 683 | "name": "stdout" 684 | } 685 | ] 686 | }, 687 | { 688 | "cell_type": "code", 689 | "metadata": { 690 | "id": "aI_NxGBNwxaU", 691 | "colab_type": "code", 692 | "colab": { 693 | "base_uri": "https://localhost:8080/", 694 | "height": 629 695 | }, 696 | "outputId": "aeb9c5fe-7073-4339-afcc-b737e8604fb6" 697 | }, 698 | "source": [ 699 | "test_gen.reset()\n", 700 | "Y_pred = model_two.predict_generator(test_gen)\n", 701 | "classes = test_gen.classes[test_gen.index_array]\n", 702 | "y_pred = np.argmax(Y_pred, axis= -1)\n", 703 | "print(sum(y_pred==classes)/800)\n", 704 | "\n", 705 | "\n", 706 | "print('Confusion Matrix')\n", 707 | "print(confusion_matrix(classes, y_pred))\n", 708 | "print('Classification Report')\n", 709 | "print(classification_report(test_gen.classes[test_gen.index_array], y_pred, target_names=target_names))" 710 | ], 711 | "execution_count": 15, 712 | "outputs": [ 713 | { 714 | "output_type": "stream", 715 | "text": [ 716 | "0.7725\n", 717 | "Confusion Matrix\n", 718 | "[[46 0 6 8 6 0 0 0 0 0 0 0 0]\n", 719 | " [ 0 21 0 0 11 1 0 0 1 0 0 0 0]\n", 720 | " [ 2 0 51 1 1 1 0 0 0 1 0 0 0]\n", 721 | " [ 0 0 1 58 1 0 1 0 0 0 3 0 0]\n", 722 | " [ 0 2 0 0 59 0 0 0 0 1 0 0 0]\n", 723 | " [ 5 0 33 2 9 14 1 0 0 0 0 0 1]\n", 724 | " [ 0 0 0 0 0 0 68 0 0 1 2 0 0]\n", 725 | " [ 0 0 0 0 0 0 0 41 1 19 9 0 2]\n", 726 | " [ 0 0 0 0 0 0 0 2 26 1 0 8 0]\n", 727 | " [ 0 0 0 0 0 0 0 0 1 58 4 1 1]\n", 728 | " [ 0 0 0 0 0 0 0 1 0 3 65 0 0]\n", 729 | " [ 0 0 0 0 0 0 0 2 6 4 0 56 0]\n", 730 | " [ 0 0 0 0 0 0 0 0 1 12 1 1 55]]\n", 731 | "Classification Report\n", 732 | " precision recall f1-score support\n", 733 | "\n", 734 | " BB 0.87 0.70 0.77 66\n", 735 | " BK 0.91 0.62 0.74 34\n", 736 | " BN 0.56 0.89 0.69 57\n", 737 | " BP 0.84 0.91 0.87 64\n", 738 | " BQ 0.68 0.95 0.79 62\n", 739 | " BR 0.88 0.22 0.35 65\n", 740 | " Empty 0.97 0.96 0.96 71\n", 741 | " WB 0.89 0.57 0.69 72\n", 742 | " WK 0.72 0.70 0.71 37\n", 743 | " WN 0.58 0.89 0.70 65\n", 744 | " WP 0.77 0.94 0.85 69\n", 745 | " WQ 0.85 0.82 0.84 68\n", 746 | " WR 0.93 0.79 0.85 70\n", 747 | "\n", 748 | " accuracy 0.77 800\n", 749 | " macro avg 0.80 0.77 0.76 800\n", 750 | "weighted avg 0.81 0.77 0.76 800\n", 751 | "\n" 752 | ], 753 | "name": "stdout" 754 | } 755 | ] 756 | }, 757 | { 758 | "cell_type": "code", 759 | "metadata": { 760 | "id": "loPv70m_zS1_", 761 | "colab_type": "code", 762 | "colab": {} 763 | }, 764 | "source": [ 765 | "" 766 | ], 767 | "execution_count": 15, 768 | "outputs": [] 769 | } 770 | ] 771 | } --------------------------------------------------------------------------------