rajesh-1902 commited on
Commit
2eb6cd8
·
verified ·
1 Parent(s): 7788363

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,11 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ model_hindi/snapshot-1.data-00000-of-00001 filter=lfs diff=lfs merge=lfs -text
37
+ model_hindi/snapshot-1.meta filter=lfs diff=lfs merge=lfs -text
38
+ model_hindi/snapshot-2.data-00000-of-00001 filter=lfs diff=lfs merge=lfs -text
39
+ model_hindi/snapshot-2.meta filter=lfs diff=lfs merge=lfs -text
40
+ model_hindi/snapshot-4.data-00000-of-00001 filter=lfs diff=lfs merge=lfs -text
41
+ model_hindi/snapshot-4.meta filter=lfs diff=lfs merge=lfs -text
42
+ model_hindi/snapshot-5.data-00000-of-00001 filter=lfs diff=lfs merge=lfs -text
43
+ model_hindi/snapshot-5.meta filter=lfs diff=lfs merge=lfs -text
DataLoader_Hindi_v2.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ from __future__ import division
3
+ from __future__ import print_function
4
+
5
+ import os
6
+ import gc
7
+ import numpy as np
8
+ import cv2
9
+ from collections import namedtuple
10
+ from datasets import load_dataset
11
+
12
+ from SamplePreprocessor_Hindi_v2 import preprocess
13
+
14
+ Batch = namedtuple("Batch", ["imgs", "gtTexts"])
15
+
16
+
17
+ class DataLoader:
18
+
19
+ def __init__(self,
20
+ dataset_name: str,
21
+ images_dir: str,
22
+ batchSize: int,
23
+ imgSize: tuple,
24
+ maxTextLen: int,
25
+ charList: str,
26
+ dataAugmentation: bool = False,
27
+ val_split: float = 0.1):
28
+
29
+ self.batchSize = batchSize
30
+ self.imgSize = imgSize
31
+ self.maxTextLen = maxTextLen
32
+ self.charList = charList
33
+ self.dataAugmentation = dataAugmentation
34
+ self.images_dir = images_dir
35
+ self.charSet = set(charList)
36
+
37
+ print(f"[DataLoader] Loading dataset: {dataset_name}")
38
+
39
+ dataset = load_dataset(dataset_name, split='train')
40
+ dataset = dataset.shuffle(seed=42)
41
+
42
+ split_idx = int(len(dataset) * (1 - val_split))
43
+
44
+ self.train_dataset = dataset.select(range(split_idx))
45
+ self.val_dataset = dataset.select(range(split_idx, len(dataset)))
46
+
47
+ print(f"[DataLoader] Train: {len(self.train_dataset)}")
48
+ print(f"[DataLoader] Val: {len(self.val_dataset)}")
49
+
50
+ self._current_dataset = None
51
+ self._idx = 0
52
+
53
+ def _load_and_preprocess_image(self, img_path, augment):
54
+ img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
55
+ if img is None:
56
+ img = np.zeros([self.imgSize[1], self.imgSize[0]], dtype=np.uint8)
57
+ return preprocess(img, self.imgSize, dataAugmentation=augment)
58
+
59
+ def trainSet(self, subset_size=None):
60
+ dataset = self.train_dataset.shuffle(seed=None)
61
+
62
+ if subset_size is not None:
63
+ subset_size = min(subset_size, len(dataset))
64
+ indices = np.random.choice(len(dataset), subset_size, replace=False)
65
+ dataset = dataset.select(indices)
66
+ print(f"[DataLoader] Training on {subset_size} samples")
67
+ else:
68
+ print(f"[DataLoader] Training on FULL dataset")
69
+
70
+ self._current_dataset = dataset
71
+ self._idx = 0
72
+
73
+ def validationSet(self):
74
+ self._current_dataset = self.val_dataset
75
+ self._idx = 0
76
+
77
+ def hasNext(self):
78
+ return self._idx < len(self._current_dataset)
79
+
80
+ def getIteratorInfo(self):
81
+ """Return current batch index and total batches."""
82
+ total = max(1, (len(self._current_dataset) + self.batchSize - 1) // self.batchSize)
83
+ current = self._idx // self.batchSize + 1
84
+ return (current, total)
85
+
86
+ def getNext(self):
87
+ end = min(self._idx + self.batchSize, len(self._current_dataset))
88
+ batch_indices = range(self._idx, end)
89
+ self._idx = end
90
+
91
+ imgs = []
92
+ gtTexts = []
93
+
94
+ for idx in batch_indices:
95
+ sample = self._current_dataset[idx]
96
+
97
+ augment = (self._current_dataset != self.val_dataset)
98
+
99
+ img = np.array(sample["image"].convert("L"))
100
+ img = preprocess(img, self.imgSize, dataAugmentation=augment)
101
+
102
+ imgs.append(img)
103
+ gtTexts.append(sample['text'])
104
+ return Batch(imgs=imgs, gtTexts=gtTexts)
105
+
106
+ def cleanup(self):
107
+ gc.collect()
Model_Hindi_v2.py ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Model_Hindi_v2.py — FAST & BALANCED VERSION
4
+ --------------------------------------------------------------
5
+ Optimized for speed with balanced regularization.
6
+
7
+ KEY CHANGES:
8
+ ✅ Larger batch size (16-32) - No gradient accumulation
9
+ ✅ Label smoothing (0.05 instead of 0.1) - Gentler
10
+ ✅ Moderate dropout (0.3) - Not too strong
11
+ ✅ Faster training
12
+ """
13
+
14
+ from __future__ import division
15
+ from __future__ import print_function
16
+
17
+ import sys
18
+ import numpy as np
19
+ import tensorflow as tf
20
+ from tensorflow.keras.layers import LSTM, Dense, Bidirectional
21
+
22
+ tf.compat.v1.disable_eager_execution()
23
+
24
+
25
+ class DecoderType:
26
+ BestPath = 0
27
+ BeamSearch = 1
28
+
29
+
30
+ class Model:
31
+ """CRNN+CTC optimized for fast training."""
32
+
33
+ # ✅ LARGER batch size for speed
34
+ batchSize = 16 # Can increase to 32 if GPU allows
35
+
36
+ imgSize = (900, 64)
37
+ maxTextLen = 128
38
+ CNN_TIME_STEPS = 225
39
+
40
+ def __init__(self, charList, decoderType=DecoderType.BestPath,
41
+ mustRestore=False, lastEpoch=0):
42
+
43
+ self.charList = charList
44
+ self.decoderType = decoderType
45
+ self.mustRestore = mustRestore
46
+ self.snapID = 0
47
+ self.lastEpoch = lastEpoch
48
+
49
+ self.SPACE_IDX = charList.index(' ') if ' ' in charList else 1
50
+
51
+ # Placeholders
52
+ self.inputImgs = tf.compat.v1.placeholder(
53
+ tf.float32,
54
+ shape=(None, Model.imgSize[0], Model.imgSize[1]),
55
+ name='input_imgs')
56
+
57
+ self.isTraining = tf.compat.v1.placeholder_with_default(
58
+ False, shape=(), name='is_training')
59
+
60
+ self.dropout_rate = tf.compat.v1.placeholder_with_default(
61
+ 0.0, shape=(), name='dropout_rate')
62
+
63
+ self.setupCNN()
64
+ self.setupRNN()
65
+ self.setupCTC()
66
+
67
+ self.learningRate = tf.compat.v1.placeholder(
68
+ tf.float32, shape=[], name='learning_rate')
69
+
70
+ self.setupOptimizer()
71
+ (self.sess, self.saver) = self.setupTF()
72
+
73
+ # ── BatchNorm ─────────────────────────────────────────────────────────────
74
+ def _batch_norm(self, x, name):
75
+ """BatchNorm layer."""
76
+ channels = x.shape[-1]
77
+
78
+ gamma = tf.Variable(tf.ones([channels]), trainable=True, name=f'{name}_gamma')
79
+ beta = tf.Variable(tf.zeros([channels]), trainable=True, name=f'{name}_beta')
80
+ moving_mean = tf.Variable(tf.zeros([channels]), trainable=False, name=f'{name}_moving_mean')
81
+ moving_var = tf.Variable(tf.ones([channels]), trainable=False, name=f'{name}_moving_var')
82
+
83
+ decay = 0.99
84
+ eps = 1e-5
85
+
86
+ def train_mode():
87
+ mean, var = tf.nn.moments(x, axes=[0, 1, 2])
88
+ update_mean = tf.compat.v1.assign(
89
+ moving_mean, moving_mean * decay + mean * (1 - decay))
90
+ update_var = tf.compat.v1.assign(
91
+ moving_var, moving_var * decay + var * (1 - decay))
92
+ with tf.control_dependencies([update_mean, update_var]):
93
+ return tf.nn.batch_normalization(x, mean, var, beta, gamma, eps)
94
+
95
+ def infer_mode():
96
+ return tf.nn.batch_normalization(
97
+ x, moving_mean, moving_var, beta, gamma, eps)
98
+
99
+ return tf.cond(self.isTraining, train_mode, infer_mode)
100
+
101
+ # ── CNN ───────────────────────────────────────────────────────────────────
102
+ def setupCNN(self):
103
+ """5-block CNN with BatchNorm."""
104
+ cnnIn4d = tf.expand_dims(self.inputImgs, axis=3)
105
+
106
+ kernelVals = [5, 5, 3, 3, 3]
107
+ featureVals = [1, 64, 128, 256, 256, 384]
108
+ poolVals = [(2, 2), (2, 2), (1, 2), (1, 2), (1, 4)]
109
+ strideVals = poolVals
110
+
111
+ pool = cnnIn4d
112
+ for i in range(len(poolVals)):
113
+ kernel = tf.Variable(
114
+ tf.random.truncated_normal(
115
+ [kernelVals[i], kernelVals[i],
116
+ featureVals[i], featureVals[i + 1]],
117
+ stddev=0.1),
118
+ name=f'cnn_kernel_{i}')
119
+
120
+ conv = tf.nn.conv2d(pool, kernel, padding='SAME', strides=(1, 1, 1, 1))
121
+ conv = self._batch_norm(conv, name=f'bn_{i}')
122
+ relu = tf.nn.relu(conv)
123
+
124
+ pool = tf.nn.max_pool(
125
+ relu,
126
+ ksize = (1, poolVals[i][0], poolVals[i][1], 1),
127
+ strides = (1, strideVals[i][0], strideVals[i][1], 1),
128
+ padding = 'VALID')
129
+
130
+ self.cnnOut4d = pool
131
+ print('[Model] CNN output shape:', pool.get_shape())
132
+
133
+ # ── RNN ───────────────────────────────────────────────────────────────────
134
+ def setupRNN(self):
135
+ """
136
+ Two stacked BiLSTMs with MODERATE dropout.
137
+
138
+ ✅ Dropout 0.3 (not 0.5) - Balanced regularization
139
+ """
140
+ rnnIn3d = tf.squeeze(self.cnnOut4d, axis=[2])
141
+
142
+ numHidden = 384
143
+
144
+ lstm1 = Bidirectional(
145
+ LSTM(numHidden, return_sequences=True),
146
+ name='bilstm_1')
147
+
148
+ lstm2 = Bidirectional(
149
+ LSTM(numHidden, return_sequences=True),
150
+ name='bilstm_2')
151
+
152
+ x = lstm1(rnnIn3d)
153
+ # ✅ Moderate dropout (0.3, not 0.5)
154
+ x = tf.nn.dropout(x, rate=self.dropout_rate)
155
+ x = lstm2(x)
156
+ x = tf.nn.dropout(x, rate=self.dropout_rate)
157
+
158
+ dense = Dense(len(self.charList) + 1, name='output_dense')
159
+ self.rnnOut3d = dense(x)
160
+ print('[Model] RNN output shape:', self.rnnOut3d.shape)
161
+
162
+ # ── CTC ───────────────────────────────────────────────────────────────────
163
+ def setupCTC(self):
164
+ """CTC loss with GENTLE label smoothing."""
165
+ num_classes = len(self.charList) + 1
166
+ init_bias = np.zeros(num_classes, dtype=np.float32)
167
+ init_bias[self.SPACE_IDX] = 1.0
168
+
169
+ self.space_logit_bias = tf.Variable(
170
+ initial_value=init_bias,
171
+ trainable=True,
172
+ name='space_logit_bias',
173
+ dtype=tf.float32)
174
+
175
+ self.ctcIn3dTBC_raw = tf.transpose(self.rnnOut3d, [1, 0, 2])
176
+ self.ctcIn3dTBC = self.ctcIn3dTBC_raw + self.space_logit_bias
177
+
178
+ self.gtTexts = tf.SparseTensor(
179
+ tf.compat.v1.placeholder(tf.int64, shape=[None, 2], name='gt_indices'),
180
+ tf.compat.v1.placeholder(tf.int32, shape=[None], name='gt_values'),
181
+ tf.compat.v1.placeholder(tf.int64, shape=[2], name='gt_shape'))
182
+
183
+ self.seqLen = tf.compat.v1.placeholder(
184
+ tf.int32, shape=[None], name='seq_len')
185
+
186
+ # ✅ GENTLE label smoothing (0.05 instead of 0.1)
187
+ self.label_smoothing = 0.05
188
+
189
+ ctc_loss_per_sample = tf.compat.v1.nn.ctc_loss(
190
+ labels = self.gtTexts,
191
+ inputs = self.ctcIn3dTBC,
192
+ sequence_length = self.seqLen,
193
+ ctc_merge_repeated = True,
194
+ ignore_longer_outputs_than_inputs = True)
195
+
196
+ # Space weighting
197
+ SPACE_WEIGHT = 2.0
198
+ batch_size_dyn = tf.shape(ctc_loss_per_sample)[0]
199
+
200
+ gt_values = self.gtTexts.values
201
+ gt_indices = self.gtTexts.indices
202
+ batch_idx = gt_indices[:, 0]
203
+
204
+ is_space = tf.cast(tf.equal(gt_values, self.SPACE_IDX), tf.float32)
205
+
206
+ spaces_per_sample = tf.math.unsorted_segment_sum(
207
+ is_space, batch_idx, num_segments=batch_size_dyn)
208
+ labels_per_sample = tf.math.unsorted_segment_sum(
209
+ tf.ones_like(is_space), batch_idx, num_segments=batch_size_dyn)
210
+
211
+ space_fraction = spaces_per_sample / tf.maximum(labels_per_sample, 1.0)
212
+ sample_weights = 1.0 + (SPACE_WEIGHT - 1.0) * space_fraction
213
+
214
+ # Apply gentle label smoothing
215
+ smoothed_loss = ctc_loss_per_sample * (1.0 - self.label_smoothing) + \
216
+ self.label_smoothing * tf.ones_like(ctc_loss_per_sample)
217
+
218
+ self.loss = tf.reduce_mean(smoothed_loss * sample_weights)
219
+
220
+ # Saved-CTC path
221
+ self.savedCtcInput = tf.compat.v1.placeholder(
222
+ tf.float32,
223
+ shape=[Model.CNN_TIME_STEPS, None, len(self.charList) + 1],
224
+ name='saved_ctc_input')
225
+
226
+ self.lossPerElement = tf.compat.v1.nn.ctc_loss(
227
+ labels = self.gtTexts,
228
+ inputs = self.savedCtcInput,
229
+ sequence_length = self.seqLen,
230
+ ctc_merge_repeated = True)
231
+
232
+ # Decoder
233
+ if self.decoderType == DecoderType.BestPath:
234
+ self.decoder = tf.nn.ctc_greedy_decoder(
235
+ inputs=self.ctcIn3dTBC, sequence_length=self.seqLen)
236
+ elif self.decoderType == DecoderType.BeamSearch:
237
+ self.decoder = tf.nn.ctc_beam_search_decoder(
238
+ inputs = self.ctcIn3dTBC,
239
+ sequence_length= self.seqLen,
240
+ beam_width = 100,
241
+ merge_repeated = False)
242
+
243
+ # ── Optimizer ─────────────────────────────────────────────────────────────
244
+ def setupOptimizer(self):
245
+ """Simple Adam optimizer (no gradient accumulation needed)."""
246
+ optimizer = tf.compat.v1.train.AdamOptimizer(self.learningRate)
247
+
248
+ # Get gradients
249
+ tvars = tf.compat.v1.trainable_variables()
250
+ grads = tf.gradients(self.loss, tvars)
251
+
252
+ # Clip gradients
253
+ grads, _ = tf.clip_by_global_norm(grads, 5.0)
254
+
255
+ # Apply gradients
256
+ update_ops = tf.compat.v1.get_collection(tf.compat.v1.GraphKeys.UPDATE_OPS)
257
+ with tf.control_dependencies(update_ops):
258
+ self.optimizer = optimizer.apply_gradients(zip(grads, tvars))
259
+
260
+ # ── TF session ────────────────────────────────────────────────────────────
261
+ def setupTF(self):
262
+ print('Python: ', sys.version)
263
+ print('TensorFlow: ', tf.__version__)
264
+
265
+ config = tf.compat.v1.ConfigProto()
266
+ config.gpu_options.allow_growth = True
267
+
268
+ sess = tf.compat.v1.Session(config=config)
269
+ saver = tf.compat.v1.train.Saver(max_to_keep=5)
270
+
271
+ modelDir = 'model_hindi/'
272
+ latestSnapshot = tf.train.latest_checkpoint(modelDir)
273
+
274
+ if self.mustRestore and not latestSnapshot:
275
+ raise Exception('No saved model found in: ' + modelDir)
276
+
277
+ if latestSnapshot:
278
+ print('[Model] Restoring from:', latestSnapshot)
279
+ saver.restore(sess, latestSnapshot)
280
+ self.lastEpoch = int(latestSnapshot.split('-')[-1])
281
+ else:
282
+ print('[Model] Initialising fresh weights')
283
+ sess.run(tf.compat.v1.global_variables_initializer())
284
+
285
+ return (sess, saver)
286
+
287
+ # ── Helpers ───────────────────────────────────────────────────────────────
288
+ def toSparse(self, texts):
289
+ indices = []
290
+ values = []
291
+ shape = [len(texts), 0]
292
+ for (batchElement, text) in enumerate(texts):
293
+ labelStr = [self.charList.index(c) for c in text]
294
+ if len(labelStr) > shape[1]:
295
+ shape[1] = len(labelStr)
296
+ for (i, label) in enumerate(labelStr):
297
+ indices.append([batchElement, i])
298
+ values.append(label)
299
+ return (indices, values, shape)
300
+
301
+ def decoderOutputToText(self, ctcOutput, batchSize):
302
+ encodedLabelStrs = [[] for _ in range(batchSize)]
303
+ decoded = ctcOutput[0][0]
304
+ for (idx, idx2d) in enumerate(decoded.indices):
305
+ label = decoded.values[idx]
306
+ batchElement = idx2d[0]
307
+ encodedLabelStrs[batchElement].append(label)
308
+ return [str().join([self.charList[c] for c in labelStr])
309
+ for labelStr in encodedLabelStrs]
310
+
311
+ # ── Train ─────────────────────────────────────────────────────────────────
312
+ def trainBatch(self, batch, learning_rate=0.0001):
313
+ """Train on one batch (NO gradient accumulation)."""
314
+ numBatchElements = len(batch.imgs)
315
+ sparse = self.toSparse(batch.gtTexts)
316
+ feedDict = {
317
+ self.inputImgs : batch.imgs,
318
+ self.gtTexts : sparse,
319
+ self.seqLen : [Model.CNN_TIME_STEPS] * numBatchElements,
320
+ self.learningRate: learning_rate,
321
+ self.isTraining : True,
322
+ self.dropout_rate: 0.3, # ✅ Moderate dropout
323
+ }
324
+
325
+ _, lossVal = self.sess.run([self.optimizer, self.loss], feedDict)
326
+ return lossVal
327
+
328
+ # ── Infer ─────────────────────────────────────────────────────────────────
329
+ def inferBatch(self, batch, calcProbability=False, probabilityOfGT=False):
330
+ """Inference with dropout disabled."""
331
+ numBatchElements = len(batch.imgs)
332
+ evalList = [self.decoder] + ([self.ctcIn3dTBC] if calcProbability else [])
333
+ feedDict = {
334
+ self.inputImgs : batch.imgs,
335
+ self.seqLen : [Model.CNN_TIME_STEPS] * numBatchElements,
336
+ self.isTraining: False,
337
+ self.dropout_rate: 0.0,
338
+ }
339
+ evalRes = self.sess.run(evalList, feedDict)
340
+ decoded = evalRes[0]
341
+ texts = self.decoderOutputToText(decoded, numBatchElements)
342
+
343
+ probs = None
344
+ if calcProbability:
345
+ sparse = (self.toSparse(batch.gtTexts)
346
+ if probabilityOfGT else self.toSparse(texts))
347
+ ctcInput = evalRes[1]
348
+ feedDict = {
349
+ self.savedCtcInput : ctcInput,
350
+ self.gtTexts : sparse,
351
+ self.seqLen : [Model.CNN_TIME_STEPS] * numBatchElements,
352
+ self.isTraining : False,
353
+ self.dropout_rate : 0.0,
354
+ }
355
+ lossVals = self.sess.run(self.lossPerElement, feedDict)
356
+ probs = np.exp(-lossVals)
357
+
358
+ return (texts, probs)
359
+
360
+ # ── Save ───────────────────────────────────────────────────────────���──────
361
+ def save(self, epoch):
362
+ self.snapID += 1
363
+ self.saver.save(self.sess, 'model_hindi/snapshot', global_step=epoch)
364
+ print(f'[Model] Checkpoint saved (epoch {epoch})')
SamplePreprocessor_Hindi_v2.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ SamplePreprocessor_Hindi.py — MODERATE AUGMENTATION VERSION
4
+ ----------------------------------------------------------
5
+ Balanced augmentation that prevents overfitting WITHOUT making images unreadable.
6
+
7
+ KEY PRINCIPLE: Model must be able to LEARN from augmented images!
8
+ - Apply only 1-2 augmentations per image (not 5+)
9
+ - Lower probabilities (20-30% instead of 40-60%)
10
+ - Gentler strength (±3° instead of ±5°)
11
+ """
12
+
13
+ from __future__ import division
14
+ from __future__ import print_function
15
+
16
+ import random
17
+ import numpy as np
18
+ import cv2
19
+
20
+
21
+ def preprocess(img: np.ndarray,
22
+ imgSize: tuple,
23
+ dataAugmentation: bool = False) -> np.ndarray:
24
+ """
25
+ Preprocess with MODERATE augmentation.
26
+
27
+ Strategy: Apply 1-2 augmentations per image, not 5+
28
+ This prevents overfitting while keeping images learnable.
29
+ """
30
+
31
+ if img is None:
32
+ img = np.zeros([imgSize[1], imgSize[0]], dtype=np.uint8)
33
+
34
+ # ── MODERATE DATA AUGMENTATION ───────────────────────────────────────────
35
+ if dataAugmentation:
36
+
37
+ # Choose 1-2 augmentations randomly (not all of them!)
38
+ num_augs = random.choice([0, 1, 1, 2]) # Mostly 1, sometimes 2, rarely 0
39
+ available_augs = ['rotate', 'brightness', 'blur', 'noise', 'scale', 'contrast']
40
+ selected_augs = random.sample(available_augs, min(num_augs, len(available_augs)))
41
+
42
+ # 1. ROTATION - More gentle (±3° instead of ±5°)
43
+ if 'rotate' in selected_augs:
44
+ angle = random.uniform(-3.0, 3.0) # Reduced from ±5°
45
+ h, w = img.shape
46
+ M = cv2.getRotationMatrix2D((w // 2, h // 2), angle, 1.0)
47
+ img = cv2.warpAffine(img, M, (w, h), borderMode=cv2.BORDER_REPLICATE)
48
+
49
+ # 2. BRIGHTNESS - Moderate variation
50
+ if 'brightness' in selected_augs:
51
+ delta = random.uniform(-20, 20) # Reduced from ±30
52
+ img = np.clip(img.astype(np.float32) + delta, 0, 255).astype(np.uint8)
53
+
54
+ # 3. CONTRAST - Gentle variation
55
+ if 'contrast' in selected_augs:
56
+ factor = random.uniform(0.8, 1.2) # Reduced from 0.7-1.3
57
+ img = np.clip((img.astype(np.float32) - 127.5) * factor + 127.5,
58
+ 0, 255).astype(np.uint8)
59
+
60
+ # 4. BLUR - Mild blur only
61
+ if 'blur' in selected_augs:
62
+ kernel_size = 3 # Only size 3
63
+ sigma = random.uniform(0.3, 1.0) # Reduced from 0.5-2.0
64
+ img = cv2.GaussianBlur(img, (kernel_size, kernel_size), sigma)
65
+
66
+ # 5. NOISE - Light noise
67
+ if 'noise' in selected_augs:
68
+ std = random.uniform(3, 8) # Reduced from 5-15
69
+ noise = np.random.normal(0, std, img.shape).astype(np.float32)
70
+ img = np.clip(img.astype(np.float32) + noise, 0, 255).astype(np.uint8)
71
+
72
+ # 6. SCALING - Small scale variation
73
+ if 'scale' in selected_augs:
74
+ scale = random.uniform(0.9, 1.1) # Reduced from 0.85-1.15
75
+ h, w = img.shape
76
+ new_h = max(1, int(h * scale))
77
+ new_w = max(1, int(w * scale))
78
+ img = cv2.resize(img, (new_w, new_h))
79
+
80
+ # ── Resize to target canvas ──────────────────────────────────────────────
81
+ (wt, ht) = imgSize
82
+ (h, w) = img.shape
83
+
84
+ f = max(w / wt, h / ht)
85
+ new_w = max(1, min(wt, int(w / f)))
86
+ new_h = max(1, min(ht, int(h / f)))
87
+
88
+ img = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)
89
+
90
+ # White canvas
91
+ canvas = np.ones([ht, wt], dtype=np.uint8) * 255
92
+ canvas[0:new_h, 0:new_w] = img
93
+
94
+ # ── Transpose for TF ──────────────────────────────────────────────────────
95
+ transposed = cv2.transpose(canvas)
96
+
97
+ # ── Normalization ─────────────────────────────────────────────────────────
98
+ (m, s) = cv2.meanStdDev(transposed)
99
+ m = m[0][0]
100
+ s = s[0][0]
101
+ normalised = (transposed - m) / (s + 1e-8)
102
+
103
+ return normalised.astype(np.float32)
build_charlist_hindi.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ build_charlist_hindi.py
4
+ -----------------------
5
+ Build charList.txt from Hindi OCR dataset.
6
+
7
+ Extracts all unique characters from the 'text' column in data.csv
8
+ and creates a character list for CTC training.
9
+
10
+ Usage:
11
+ python build_charlist_hindi.py --csv path/to/data.csv
12
+
13
+ Output:
14
+ ../model_hindi/charList.txt
15
+ """
16
+
17
+ import os
18
+ import argparse
19
+ import pandas as pd
20
+
21
+
22
+ def build_charlist(csv_path: str, output_path: str):
23
+ """
24
+ Extract all unique characters from CSV and build charList.txt.
25
+
26
+ Format:
27
+ Index 0: '-' (CTC blank token)
28
+ Index 1: ' ' (space - critical for word boundaries)
29
+ Index 2+: All other characters in unicode order
30
+ """
31
+ print(f"Loading dataset from: {csv_path}")
32
+
33
+ # Load CSV
34
+ df = pd.read_csv(csv_path)
35
+ print(f"Total samples: {len(df)}")
36
+
37
+ # Extract all unique characters
38
+ all_chars = set()
39
+
40
+ for text in df['text']:
41
+ all_chars.update(set(text))
42
+
43
+ # Remove empty string if present
44
+ all_chars.discard("")
45
+
46
+ print(f"\nFound {len(all_chars)} unique characters")
47
+
48
+ # Separate space from other characters
49
+ space_char = " "
50
+ other_chars = [c for c in all_chars if c != space_char]
51
+
52
+ # Sort other characters by unicode value
53
+ other_chars.sort(key=lambda c: ord(c))
54
+
55
+ # Build final charlist:
56
+ # Index 0 = '-' (CTC blank token) — MUST be first
57
+ # Index 1 = ' ' (space — critical for word boundaries)
58
+ # Index 2+ = all other characters in unicode order
59
+ final_charlist = "-" + space_char + "".join(other_chars)
60
+
61
+ # Make output directory if needed
62
+ os.makedirs(os.path.dirname(output_path), exist_ok=True)
63
+
64
+ # Save
65
+ with open(output_path, "w", encoding="utf-8") as f:
66
+ f.write(final_charlist)
67
+
68
+ print(f"\n✅ charList.txt saved to: {output_path}")
69
+ print(f" Total characters: {len(final_charlist)}")
70
+ print(f" Index 0 (CTC blank): '-'")
71
+ print(f" Index 1 (space): ' '")
72
+ print(f"\n First 50 characters:")
73
+ print(f" {repr(final_charlist[:50])}")
74
+ print(f"\n Sample Devanagari characters:")
75
+
76
+ # Show some sample Devanagari characters
77
+ devanagari_chars = [c for c in other_chars if '\u0900' <= c <= '\u097F']
78
+ print(f" {repr(''.join(devanagari_chars[:30]))}")
79
+
80
+ # Statistics
81
+ print(f"\n📊 Character set statistics:")
82
+ print(f" Devanagari consonants/vowels: {len(devanagari_chars)}")
83
+ print(f" Digits: {sum(c.isdigit() for c in other_chars)}")
84
+ print(f" Punctuation: {sum(not c.isalnum() for c in other_chars)}")
85
+
86
+ # Show character distribution
87
+ print(f"\n Unicode ranges:")
88
+ ranges = {
89
+ 'Devanagari': (0x0900, 0x097F),
90
+ 'ASCII': (0x0000, 0x007F),
91
+ 'Latin Extended': (0x0080, 0x00FF),
92
+ }
93
+
94
+ for name, (start, end) in ranges.items():
95
+ count = sum(start <= ord(c) <= end for c in other_chars)
96
+ if count > 0:
97
+ print(f" {name}: {count} characters")
98
+
99
+
100
+ def main():
101
+ parser = argparse.ArgumentParser(
102
+ description="Build character list for Hindi OCR dataset")
103
+ parser.add_argument("--csv", type=str, required=True,
104
+ help="Path to data.csv file")
105
+ parser.add_argument("--output", type=str,
106
+ default="../model_hindi/charList.txt",
107
+ help="Output path for charList.txt")
108
+
109
+ args = parser.parse_args()
110
+
111
+ if not os.path.exists(args.csv):
112
+ print(f"❌ Error: CSV file not found: {args.csv}")
113
+ return
114
+
115
+ build_charlist(args.csv, args.output)
116
+
117
+
118
+ if __name__ == "__main__":
119
+ main()
main_hindi_v2.py ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ main_hindi_v2.py — FAST TRAINING VERSION
4
+ -------------------------------------------------------------
5
+ Optimized training with proper learning rate schedule.
6
+
7
+ KEY IMPROVEMENTS:
8
+ ✅ Warmup + cosine decay learning rate
9
+ ✅ Larger batch size = faster training
10
+ ✅ Less verbose logging
11
+ ✅ Better early stopping
12
+ """
13
+
14
+ from __future__ import division
15
+ from __future__ import print_function
16
+
17
+ import os
18
+ import sys
19
+ import argparse
20
+ import codecs
21
+ import gc
22
+ import json
23
+ import time
24
+ from datetime import datetime
25
+ import math
26
+
27
+ import cv2
28
+ import numpy as np
29
+ import tensorflow as tf
30
+ import editdistance
31
+
32
+ from DataLoader_Hindi_v2 import DataLoader, Batch
33
+ from Model_Hindi_v2 import Model, DecoderType
34
+ from SamplePreprocessor_Hindi_v2 import preprocess
35
+
36
+ tf.compat.v1.disable_eager_execution()
37
+
38
+ # ── Paths ─────────────────────────────────────────────────────────────────────
39
+ class FilePaths:
40
+ fnCharList = "model_hindi/charList.txt"
41
+ fnAccuracy = "model_hindi/accuracy.txt"
42
+ fnMetrics = "model_hindi/metrics.json"
43
+
44
+
45
+ # ── Learning rate schedule ────────────────────────────────────────────────────
46
+ def get_learning_rate(epoch, total_epochs=50):
47
+ """
48
+ Warmup + Cosine decay learning rate.
49
+
50
+ Better than step decay for convergence.
51
+ """
52
+ warmup_epochs = 3
53
+ base_lr = 0.001
54
+ min_lr = 0.00001
55
+
56
+ if epoch < warmup_epochs:
57
+ # Linear warmup
58
+ return base_lr * (epoch + 1) / warmup_epochs
59
+ else:
60
+ # Cosine decay
61
+ progress = (epoch - warmup_epochs) / (total_epochs - warmup_epochs)
62
+ return min_lr + (base_lr - min_lr) * 0.5 * (1 + math.cos(math.pi * progress))
63
+
64
+
65
+ # ── Metrics tracking ──────────────────────────────────────────────────────────
66
+ class MetricsTracker:
67
+ """Track training metrics."""
68
+
69
+ def __init__(self, filepath):
70
+ self.filepath = filepath
71
+ self.metrics = {
72
+ 'train_loss': [],
73
+ 'val_cer': [],
74
+ 'val_word_acc': [],
75
+ 'learning_rate': [],
76
+ 'epochs': [],
77
+ 'epoch_times': []
78
+ }
79
+
80
+ if os.path.exists(filepath):
81
+ try:
82
+ with open(filepath, 'r') as f:
83
+ self.metrics = json.load(f)
84
+ except:
85
+ pass
86
+
87
+ def add_epoch(self, epoch, train_loss, val_cer, val_word_acc, lr, epoch_time):
88
+ """Add metrics for an epoch."""
89
+ self.metrics['epochs'].append(epoch)
90
+ self.metrics['train_loss'].append(float(train_loss))
91
+ self.metrics['val_cer'].append(float(val_cer))
92
+ self.metrics['val_word_acc'].append(float(val_word_acc))
93
+ self.metrics['learning_rate'].append(float(lr))
94
+ self.metrics['epoch_times'].append(float(epoch_time))
95
+ self.save()
96
+
97
+ def save(self):
98
+ """Save metrics."""
99
+ with open(self.filepath, 'w') as f:
100
+ json.dump(self.metrics, f, indent=2)
101
+
102
+ def get_best_cer(self):
103
+ """Get best CER."""
104
+ if not self.metrics['val_cer']:
105
+ return float('inf')
106
+ return min(self.metrics['val_cer'])
107
+
108
+
109
+ # ── Training ──────────────────────────────────────────────────────────────────
110
+ def train(model: Model, loader: DataLoader, total_epochs=50):
111
+ """Train with early stopping."""
112
+ epoch = model.lastEpoch
113
+
114
+ metrics_tracker = MetricsTracker(FilePaths.fnMetrics)
115
+ bestCharErrorRate = metrics_tracker.get_best_cer()
116
+
117
+ noImprovementSince = 0
118
+ earlyStopping = 10
119
+
120
+ while epoch < total_epochs:
121
+ epoch += 1
122
+ epoch_start = time.time()
123
+
124
+ print(f"\n{'='*80}")
125
+ print(f"Epoch {epoch}/{total_epochs}")
126
+ print(f"{'='*80}")
127
+
128
+ lr = get_learning_rate(epoch, total_epochs)
129
+ print(f"Learning rate: {lr:.6f}")
130
+
131
+ # ── Train ─────────────────────────────────────────────────────────────
132
+ print("Training...")
133
+ loader.trainSet(subset_size=10000)
134
+ epoch_loss = []
135
+
136
+ batch_count = 0
137
+ while loader.hasNext():
138
+ batch = loader.getNext()
139
+ loss = model.trainBatch(batch, learning_rate=lr)
140
+ epoch_loss.append(loss)
141
+ batch_count += 1
142
+
143
+ # Print progress every 50 batches
144
+ if batch_count % 50 == 0:
145
+ iterInfo = loader.getIteratorInfo()
146
+ print(f" Batch {iterInfo[0]:>4}/{iterInfo[1]} loss={loss:.4f}")
147
+
148
+ mean_loss = np.mean(epoch_loss)
149
+ epoch_time = time.time() - epoch_start
150
+ print(f" Epoch loss: {mean_loss:.4f} Time: {epoch_time/60:.1f}min")
151
+
152
+ loader.cleanup()
153
+ gc.collect()
154
+
155
+ # ── Validate ──────────────────────────────────────────────────────────
156
+ charErrorRate, wordAccuracy = validate(model, loader)
157
+
158
+ # Track metrics
159
+ metrics_tracker.add_epoch(epoch, mean_loss, charErrorRate,
160
+ wordAccuracy, lr, epoch_time)
161
+
162
+ # Checkpoint
163
+ if charErrorRate < bestCharErrorRate:
164
+ improvement = (bestCharErrorRate - charErrorRate) * 100
165
+ print(f"✅ CER improved by {improvement:.2f}pp → Saving model")
166
+
167
+ bestCharErrorRate = charErrorRate
168
+ noImprovementSince = 0
169
+ model.save(epoch)
170
+
171
+ with open(FilePaths.fnAccuracy, "w", encoding="utf-8") as f:
172
+ f.write(f"Epoch: {epoch}\n")
173
+ f.write(f"CER: {charErrorRate*100:.2f}%\n")
174
+ f.write(f"Word Acc: {wordAccuracy*100:.2f}%\n")
175
+ f.write(f"Loss: {mean_loss:.4f}\n")
176
+ else:
177
+ print(f" No improvement ({charErrorRate*100:.2f}% vs {bestCharErrorRate*100:.2f}%)")
178
+ noImprovementSince += 1
179
+
180
+ # Save every 5 epochs
181
+ if epoch % 5 == 0:
182
+ model.save(epoch)
183
+
184
+ gc.collect()
185
+
186
+ # Early stopping
187
+ if noImprovementSince >= earlyStopping:
188
+ print(f"\nEarly stopping: No improvement for {earlyStopping} epochs")
189
+ print(f"Best CER: {bestCharErrorRate*100:.2f}%")
190
+ break
191
+
192
+
193
+ # ── Validation ────────────────────────────────────────────────────────────────
194
+ def validate(model: Model, loader: DataLoader):
195
+ """Run validation."""
196
+ print("Validating...")
197
+ loader.validationSet()
198
+
199
+ numCharErr = 0
200
+ numCharTotal = 0
201
+ numWordOK = 0
202
+ numWordTotal = 0
203
+
204
+ batch_count = 0
205
+
206
+ while loader.hasNext():
207
+ batch = loader.getNext()
208
+ (recognized, _) = model.inferBatch(batch)
209
+
210
+ for i in range(len(recognized)):
211
+ gt = batch.gtTexts[i]
212
+ pred = recognized[i]
213
+
214
+ numWordOK += 1 if gt == pred else 0
215
+ numWordTotal += 1
216
+
217
+ dist = editdistance.eval(pred, gt)
218
+ numCharErr += dist
219
+ numCharTotal += len(gt)
220
+
221
+ # Print first few predictions
222
+ if batch_count == 0 and i < 3:
223
+ status = "[✓]" if dist == 0 else f"[✗ {dist}]"
224
+ print(f" {status} GT: '{gt[:60]}'")
225
+ print(f" PRD: '{pred[:60]}'")
226
+
227
+ batch_count += 1
228
+
229
+ charErrorRate = numCharErr / max(numCharTotal, 1)
230
+ wordAccuracy = numWordOK / max(numWordTotal, 1)
231
+
232
+ print(f"\n{'='*80}")
233
+ print(f"VALIDATION: CER={charErrorRate*100:.2f}% Word Acc={wordAccuracy*100:.2f}%")
234
+ print(f"{'='*80}")
235
+
236
+ loader.cleanup()
237
+ gc.collect()
238
+
239
+ return charErrorRate, wordAccuracy
240
+
241
+
242
+ # ── Inference ─────────────────────────────────────────────────────────────────
243
+ def infer(model: Model, img_path: str) -> str:
244
+ """Inference on single image."""
245
+ img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
246
+ if img is None:
247
+ raise FileNotFoundError(f"Cannot read: {img_path}")
248
+
249
+ processed = preprocess(img, Model.imgSize, dataAugmentation=False)
250
+ batch = Batch(imgs=[processed], gtTexts=None)
251
+
252
+ (recognized, probability) = model.inferBatch(batch, calcProbability=True)
253
+
254
+ print(f"Recognised: '{recognized[0]}'")
255
+ print(f"Probability: {probability[0]:.4f}")
256
+
257
+ return recognized[0]
258
+
259
+
260
+ # ── Helpers ───────────────────────────────────────────────────────────────────
261
+ def load_char_list() -> str:
262
+ if not os.path.exists(FilePaths.fnCharList):
263
+ raise FileNotFoundError(f"charList.txt not found at {FilePaths.fnCharList}")
264
+ with codecs.open(FilePaths.fnCharList, encoding="utf-8") as f:
265
+ return f.read()
266
+
267
+
268
+ # ── Main ──────────────────────────────────────────────────────────────────────
269
+ def main():
270
+ parser = argparse.ArgumentParser(description="Hindi OCR - Fast Training")
271
+
272
+ parser.add_argument("--train", action="store_true", help="Train")
273
+ parser.add_argument("--validate", action="store_true", help="Validate")
274
+ parser.add_argument("--infer", action="store_true", help="Infer")
275
+
276
+ parser.add_argument("--dataset", type=str,
277
+ default="rajesh-1902/hindi-ocr-dataset",
278
+ help="HuggingFace dataset name")
279
+ parser.add_argument("--images", type=str, default="images/",
280
+ help="Path to images directory")
281
+ parser.add_argument("--image", type=str, default=None,
282
+ help="Image for inference")
283
+
284
+ parser.add_argument("--epochs", type=int, default=50,
285
+ help="Total epochs to train")
286
+ parser.add_argument("--beamsearch", action="store_true",
287
+ help="Use beam search decoder")
288
+
289
+ args = parser.parse_args()
290
+
291
+ decoderType = DecoderType.BeamSearch if args.beamsearch else DecoderType.BestPath
292
+ charList = load_char_list()
293
+
294
+ print(f"\nCharacter list: {len(charList)} characters")
295
+ print(f"Index 0: {repr(charList[0])} (blank)")
296
+ print(f"Index 1: {repr(charList[1])} (space)")
297
+
298
+ os.makedirs("model_hindi/", exist_ok=True)
299
+
300
+ if args.train or args.validate:
301
+ loader = DataLoader(
302
+ dataset_name=args.dataset,
303
+ images_dir=args.images,
304
+ batchSize=Model.batchSize,
305
+ imgSize=Model.imgSize,
306
+ maxTextLen=Model.maxTextLen,
307
+ charList=charList,
308
+ dataAugmentation=args.train,
309
+ val_split=0.1
310
+ )
311
+
312
+ # Save corpus
313
+
314
+ if args.train:
315
+ model = Model(charList, decoderType, mustRestore=False, lastEpoch=0)
316
+ try:
317
+ train(model, loader, total_epochs=args.epochs)
318
+ except KeyboardInterrupt:
319
+ print(f"\nTraining interrupted at epoch {model.lastEpoch}")
320
+
321
+ elif args.validate:
322
+ model = Model(charList, decoderType, mustRestore=True)
323
+ validate(model, loader)
324
+
325
+ elif args.infer:
326
+ if not args.image:
327
+ print("Error: --infer requires --image")
328
+ sys.exit(1)
329
+ model = Model(charList, decoderType, mustRestore=True)
330
+ infer(model, args.image)
331
+
332
+ else:
333
+ parser.print_help()
334
+
335
+
336
+ if __name__ == "__main__":
337
+ main()
model_hindi/accuracy.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ Epoch: 4
2
+ CER: 0.62%
3
+ Word Acc: 84.03%
4
+ Loss: 1.6097
model_hindi/charList.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ - !"#$%'()*+,-./0123456789:;=?@_`|ँंःअआइईउऊऋएऐऑओऔकखगघचछजझञटठडढणतथदधनपफबभमयरऱलवशषसह़ािीुूृॅेैॉोौ्ख़ज़ड़ढ़फ़​‌‍‏–‘’“”…₹
model_hindi/checkpoint ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ model_checkpoint_path: "snapshot-5"
2
+ all_model_checkpoint_paths: "snapshot-1"
3
+ all_model_checkpoint_paths: "snapshot-2"
4
+ all_model_checkpoint_paths: "snapshot-4"
5
+ all_model_checkpoint_paths: "snapshot-5"
model_hindi/metrics.json ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "train_loss": [
3
+ 92.00146484375,
4
+ 4.817220211029053,
5
+ 2.3719639778137207,
6
+ 1.6097475290298462,
7
+ 1.2168760299682617
8
+ ],
9
+ "val_cer": [
10
+ 0.0749026983742714,
11
+ 0.012815881115104564,
12
+ 0.018309465725618725,
13
+ 0.006242201903201177,
14
+ 0.007158420082310658
15
+ ],
16
+ "val_word_acc": [
17
+ 0.1515625,
18
+ 0.72609375,
19
+ 0.62203125,
20
+ 0.8403125,
21
+ 0.81859375
22
+ ],
23
+ "learning_rate": [
24
+ 0.0006666666666666666,
25
+ 0.001,
26
+ 0.001,
27
+ 0.0009509795896116975,
28
+ 0.0008136274519200731
29
+ ],
30
+ "epochs": [
31
+ 1,
32
+ 2,
33
+ 3,
34
+ 4,
35
+ 5
36
+ ],
37
+ "epoch_times": [
38
+ 1097.182646036148,
39
+ 1089.5408215522766,
40
+ 1088.228083372116,
41
+ 1079.393277645111,
42
+ 1083.2388920783997
43
+ ]
44
+ }
model_hindi/snapshot-1.data-00000-of-00001 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4291c4d3442d1caa732a2509144b5843896b2e424855009a89813b5ce4a5801a
3
+ size 95642112
model_hindi/snapshot-1.index ADDED
Binary file (4.06 kB). View file
 
model_hindi/snapshot-1.meta ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a8513619f1dadb01b917437682f67dd532324fd8998745c5c658058d01cced60
3
+ size 765975
model_hindi/snapshot-2.data-00000-of-00001 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:02bffa6061034b19754288469c499d40338ecf59438f1ad33866309cd6a450e0
3
+ size 95642112
model_hindi/snapshot-2.index ADDED
Binary file (4.06 kB). View file
 
model_hindi/snapshot-2.meta ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a8513619f1dadb01b917437682f67dd532324fd8998745c5c658058d01cced60
3
+ size 765975
model_hindi/snapshot-4.data-00000-of-00001 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dd04849088828eb6dcdf214571cd6645feb10c3aece45d460403e1f0c70c7a7d
3
+ size 95642112
model_hindi/snapshot-4.index ADDED
Binary file (4.06 kB). View file
 
model_hindi/snapshot-4.meta ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a8513619f1dadb01b917437682f67dd532324fd8998745c5c658058d01cced60
3
+ size 765975
model_hindi/snapshot-5.data-00000-of-00001 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b9bc0e778b55ece9933bb335fa75c8933e9a224e58ec218ddd12b65f873619cf
3
+ size 95642112
model_hindi/snapshot-5.index ADDED
Binary file (4.06 kB). View file
 
model_hindi/snapshot-5.meta ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a8513619f1dadb01b917437682f67dd532324fd8998745c5c658058d01cced60
3
+ size 765975