-
Notifications
You must be signed in to change notification settings - Fork 1
/
ChinCharRecog.py
245 lines (186 loc) · 7.74 KB
/
ChinCharRecog.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 17 03:39:35 2021
Last Updated on Wed Aug 11 02:42 2021
Author: Tyler Pruitt
"""
# Import packages
import tensorflow as tf
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import os
import cv2
# Import helper functions
def loadImages(folder):
"""
imports the images held within a folder into a list of images
"""
images, imgdict = [], {}
for filename in os.listdir(folder):
img = cv2.imread(os.path.join(folder,filename),0)
# Key is "input_suite_id_sample_id_code.jpg"
key = filename
if img is not None:
images.append(img)
imgdict[key] = img
return imgdict
def filePathCol(csvData):
"""
returns the file path of the associated image for the indices of the csv data
"""
filePath = f"input_{csvData[0]}_{csvData[1]}_{csvData[2]}.jpg"
return filePath
def displayImage(image, name=""):
"""
displays the image in grayscale with name in the title if given one
"""
if name == "":
plt.figure(frameon=False)
plt.imshow(image, cmap="gray")
plt.title(name)
plt.axis("off")
plt.show()
# Load chinese_mnist.csv data
labels = ('suite_id', 'sample_id', 'code', 'value', 'character')
characters = ('零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '百', '千', '万', '亿')
"""
For this data 100 people wrote each of the 15 characters 10 times
suite_id is for each volunteer (100 total)
sample_id is for each sample of each volunteer (10 total)
i.e. each volunteer writes each character 10 times
code is used to identify each character in their sequence
i.e. code is the ith character in order
i.e. 零 is 1, 一 is 2, 二 is 3, ... 九 is 10,
value is the numerical value of the character i.e. 5
character is the actual symbol i.e. 五
"""
csvData = pd.read_csv('chinese_mnist.csv', names=labels)
# Output csv information to screen
print(csvData.describe(), end="\n\n")
print(csvData.head(), end="\n\n")
print(csvData.tail(), end="\n\n")
# Load image data (15,000 images each 64 x 64)
directory = input("Enter directory of image data: ")
imageDict = loadImages(directory)
# Check to see how images and csv data are connected
displayImage(imageDict["input_1_1_10.jpg"], "input_1_1_10.jpg")
csvData["file_path"] = csvData.apply(filePathCol, axis=1)
print(csvData.head(), end="\n\n")
csvDataArray = np.array(csvData)
# Convert strings of numbers in csvDataArray into int type
for i in range(len(csvDataArray)):
for j in range(4):
try:
csvDataArray[i][j] = int(csvDataArray[i][j])
except:
pass
print(csvDataArray, end="\n\n")
# Split into training data (12,000 images) and testing data (3,000 images) into an 80/20 split
"""
Note:
split should be in multiples of 10% because we need to split evenly so as to incorporate all
of the 100 participants and we need to split evenly so as to incorporate all of the characters
assuming that there is no important difference between the first couple characters written and
the last couple characters written (assuming same character as just mentioned) then since each
person only wrote each character 10 times we are limited to the degree in which we can split
because 10 is a discrete number that we have to split between our training data and our test
data
"""
# Here we need to 80/20 split based on sample_id (sample_id index is 1)
invalidSplit = True
while invalidSplit:
split = float(input("Enter train/test split from 0.0 to 1.0 in increments of 0.1: "))
if split >= 0.1 and split <= 1.0:
invalidSplit = False
elif split < 0.1:
print("Train/test split must be at least 0.1 so that there is data to train on")
else:
print("Train/test split should be at most 0.9 if testing data is desire. Without testing data, 1.0 is the maximum value for train/test split")
print("Training data: " + str(100*split) + "%")
print("Testing data: " + str(100*(1 - split)) + "%")
trainData = np.empty((int(15000*split), 6), dtype=np.ndarray)
testData = np.empty((15000 - int(15000*split), 6), dtype=np.ndarray)
trainCount, testCount = 0, 0
for i in range(1, len(csvDataArray)):
if int(csvDataArray[i][1]) <= int(10*split):
trainData[trainCount] = csvDataArray[i]
trainCount += 1
else:
testData[testCount] = csvDataArray[i]
testCount += 1
trainFiles = trainData[:,5]
testFiles = testData[:,5]
trainCharacters = trainData[:,4]
testCharacters = testData[:,4]
trainLabels = trainData[:,2]
testLabels = testData[:,2]
trainLabels = np.asarray(trainLabels).astype(np.float32)
testLabels = np.asarray(testLabels).astype(np.float32)
# Subtract 1 from each train label and test label to meet input format for model
for i in range(len(trainLabels)):
trainLabels[i] -= 1
for i in range(len(testLabels)):
testLabels[i] -= 1
trainImages, testImages = np.empty((12000, 64, 64), dtype=int), np.empty((3000, 64, 64), dtype=int)
trainImgCount, testImgCount = 0, 0
for fileName in trainFiles:
trainImages[trainImgCount] = imageDict[fileName]
trainImgCount += 1
for fileName in testFiles:
testImages[testImgCount] = imageDict[fileName]
testImgCount += 1
trainImages = trainImages / 255
testImages = testImages / 255
# Training data is (trainLabels, trainImages) and testing data is (testLabels, testImages)
# Build the model
model = tf.keras.Sequential([
tf.keras.layers.Flatten(input_shape=(64, 64)),
tf.keras.layers.Dense(128, activation="relu"),
tf.keras.layers.Dense(15)
])
# Compile the model
model.compile(optimizer="adam", loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=["accuracy"])
# Train the model with the training data
invalidNumberEpochs = True
while invalidNumberEpochs:
numberEpochs = int(input("Enter the number of epochs for model training: "))
if numberEpochs > 0:
invalidNumberEpochs = False
else:
print("Number of epochs must be greater than 0")
print("Number of epochs:", numberEpochs)
model.fit(trainImages, trainLabels, epochs=numberEpochs)
if split < 1.0:
# Test the model with the testing data
print("Evaluating testing data...")
testLoss, testAccuracy = model.evaluate(testImages, testLabels, verbose=1)
print("Test accuracy: " + str(round(testAccuracy*100, 3)) + "%")
print("Test loss:", round(testLoss, 5), end="\n\n")
# Adapt model's output to output probabilities
model = tf.keras.Sequential([model, tf.keras.layers.Softmax()])
# Test model's prediction about other characters in the testing data
PredictionData = model.predict(testImages)
# Check predictions on the 50th and 500th images in the testing data and show images
barPlotAxis = (0,1,2,3,4,5,6,7,8,9,10,11,12,13,14)
print("Expected value for testImages[50]:", testCharacters[50])
print("Model's prediction on testImages[50]", characters[np.argmax(PredictionData[50])])
displayImage(testImages[50])
plt.bar(barPlotAxis, PredictionData[50])
print("Expected value for testImages[500]:", testCharacters[500])
print("Model's prediction on testImages[500]", characters[np.argmax(PredictionData[500])])
displayImage(testImages[500])
plt.bar(barPlotAxis, PredictionData[500])
else:
# Adapt model's output to output probabilities
model = tf.keras.Sequential([model, tf.keras.layers.Softmax()])
# Save the model and the probability model
model.save("model")
model.save("model.h5")
# Load the model to check that it works
newModel = tf.keras.models.load_model("model", compile=True)
# If the model needs to be compiled, then run
#newModel.compile(optimizer="adam", loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
# metrics=["accuracy"])