Heim > Artikel > Backend-Entwicklung > Sequenzanmerkung, handgeschriebener OCR-Datensatz für Kleinbuchstaben, bidirektionales RNN
Sequenzkennzeichnung, Vorhersage einer Kategorie für jeden Frame der Eingabesequenz. OCR (Optical Character Recognition, optische Zeichenerkennung).
Der OCR-Datensatz (http://ai.stanford.edu/~btaskar/ocr/), gesammelt von Rob Kassel von der MIT Spoken Language System Research Group und vorverarbeitet von Ben Taskar von der Stanford University Das Labor für künstliche Intelligenz enthält eine große Anzahl einzelner Kleinbuchstaben, wobei jedes Beispiel einem 16 x 8 Pixel großen Binärbild entspricht. Wortzeilen kombinieren Sequenzen, und die Sequenzen entsprechen Wörtern. 6800 Wörter mit einer Länge von maximal 14 Buchstaben. gzip-komprimiert, Inhalt ist eine tabulatorgetrennte Textdatei. Das Python-CSV-Modul liest direkt. Jede Zeile der Datei verfügt über ein normalisiertes Buchstabenattribut, eine ID-Nummer, eine Beschriftung, einen Pixelwert, eine nächste Buchstaben-ID-Nummer usw.
Sortieren Sie nach dem nächsten Buchstaben-ID-Wert und lesen Sie jeden Wortbuchstaben in der richtigen Reihenfolge. Briefe werden gesammelt, bis das nächste ID-entsprechende Feld nicht gesetzt wird. Neue Sequenz lesen. Füllen Sie nach dem Lesen der Zielbuchstaben und Datenpixel das Sequenzobjekt mit Nullbildern, die in das NumPy-Array aller Pixeldaten der beiden größeren Zielbuchstaben aufgenommen werden können.
Die Softmax-Ebene wird zwischen den Zeitschritten gemeinsam genutzt. Die Daten- und Zielarrays enthalten Sequenzen, einen Bildrahmen für jeden Zielbuchstaben. RNN-Erweiterung, die jeder Buchstabenausgabe einen Softmax-Klassifikator hinzufügt. Der Klassifikator wertet Vorhersagen für jeden Datenrahmen und nicht für die gesamte Sequenz aus. Berechnen Sie die Sequenzlänge. Allen Frames wird eine Softmax-Ebene hinzugefügt: Entweder werden allen Frames mehrere unterschiedliche Klassifikatoren hinzugefügt, oder alle Frames verwenden denselben Klassifikator. Bei einem gemeinsamen Klassifikator werden die Gewichte während des Trainings mehrmals für jeden Buchstaben des Trainingsworts angepasst. Eine vollständig verbundene Schichtgewichtsmatrix mit der Dimension „batch_size*in_size*out_size“. Nun ist es notwendig, die Gewichtsmatrix in den beiden Eingabedimensionen „batch_size“ und „sequence_steps“ zu aktualisieren. Lassen Sie die Eingabe (RNN-Ausgabeaktivitätswert) auf die Form „batch_size*sequence_steps*in_size“ reduzieren. Die Gewichtsmatrix wird zu einem größeren Datenstapel. Das Ergebnis ist nicht abgeflacht.
Kostenfunktion, jeder Frame der Sequenz hat ein vorhergesagtes Zielpaar, gemittelt in der entsprechenden Dimension. tf.reduce_mean normalisiert nach Tensorlänge (maximale Länge der Sequenz) kann nicht verwendet werden. Es ist erforderlich, entsprechend der tatsächlichen Sequenzlänge zu normalisieren und tf.reduce_sum und den Divisionsoperationsmittelwert manuell aufzurufen.
Verlustfunktion, tf.argmax gilt für Achse 2 und nicht für Achse 1, jeder Frame wird gefüllt und der Mittelwert wird basierend auf der tatsächlichen Länge der Sequenz berechnet. tf.reduce_mean ermittelt den Mittelwert aller Wörter in den Stapeldaten.
Die automatische Ableitungsberechnung von TensorFlow kann dieselbe Optimierungsoperation für die Sequenzklassifizierung verwenden und muss lediglich eine neue Kostenfunktion ersetzen. Schneiden Sie alle RNN-Gradienten ab, um Trainingsdivergenzen und negative Auswirkungen zu vermeiden.
Trainieren Sie das Modell, get_sataset lädt das Handschriftbild, die Vorverarbeitung und den One-Hot-Codierungsvektor von Kleinbuchstaben herunter. Stören Sie nach dem Zufallsprinzip die Reihenfolge der Daten und teilen Sie sie in Trainingssätze und Testsätze auf.
Zwischen benachbarten Buchstaben eines Wortes besteht eine Abhängigkeitsbeziehung (oder gegenseitige Information), und RNN speichert alle Eingabeinformationen desselben Wortes im impliziten Aktivitätswert. Für die Klassifizierung der ersten paar Buchstaben verfügt das Netzwerk nicht über viele Eingaben, um zusätzliche Informationen abzuleiten, und bidirektionales RNN überwindet diese Mängel.
Zwei RNNs beobachten die Eingabesequenz, einer liest Wörter vom linken Ende in der üblichen Reihenfolge und der andere liest Wörter vom rechten Ende in umgekehrter Reihenfolge. Für jeden Zeitschritt werden zwei Ausgabeaktivitätswerte erhalten. Spleißen vor dem Senden an die gemeinsam genutzte Softmax-Ebene. Der Klassifikator erhält aus jedem Buchstaben vollständige Wortinformationen. tf.model.rnn.bidirectional_rnn ist implementiert.
Bidirektionales RNN implementieren. Teilen Sie die Vorhersageattribute in zwei Funktionen auf und konzentrieren Sie sich auf weniger Inhalte. Die Funktion _shared_softmax übergibt die Tensordaten der Funktion, um auf die Eingabegröße zu schließen. Durch die Wiederverwendung anderer Architekturfunktionen verwendet dieselbe Glättungstechnik in allen Zeitschritten dieselbe Softmax-Schicht. rnn.dynamic_rnn erstellt zwei RNNs.
Sequenzumkehr ist einfacher als die Implementierung der neuen Reverse-Pass-RNN-Operation. Die Funktion tf.reverse_sequence kehrt die sequence_lengths-Frames in den Frame-Daten um. Datenflussdiagrammknoten haben Namen. Der Bereichsparameter ist der Bereichsname der Variable rnn_dynamic_cell, und der Standardwert ist RNN. Die beiden Parameter sind für RNN unterschiedlich und erfordern unterschiedliche Domänen.
Die umgekehrte Sequenz wird in das Rückwärts-RNN eingespeist, und die Netzwerkausgabe wird umgekehrt und an der Vorwärtsausgabe ausgerichtet. Verketten Sie zwei Tensoren entlang der Ausgabedimension des RNN-Neurons und kehren Sie zurück. Das bidirektionale RNN-Modell schneidet besser ab.
import gzipimport csvimport numpy as npfrom helpers import downloadclass OcrDataset: URL = 'http://ai.stanford.edu/~btaskar/ocr/letter.data.gz'def __init__(self, cache_dir): path = download(type(self).URL, cache_dir) lines = self._read(path) data, target = self._parse(lines) self.data, self.target = self._pad(data, target) @staticmethoddef _read(filepath): with gzip.open(filepath, 'rt') as file_: reader = csv.reader(file_, delimiter='\t') lines = list(reader)return lines @staticmethoddef _parse(lines): lines = sorted(lines, key=lambda x: int(x[0])) data, target = [], [] next_ = Nonefor line in lines:if not next_: data.append([]) target.append([])else:assert next_ == int(line[0]) next_ = int(line[2]) if int(line[2]) > -1 else None pixels = np.array([int(x) for x in line[6:134]]) pixels = pixels.reshape((16, 8)) data[-1].append(pixels) target[-1].append(line[1])return data, target @staticmethoddef _pad(data, target): max_length = max(len(x) for x in target) padding = np.zeros((16, 8)) data = [x + ([padding] * (max_length - len(x))) for x in data] target = [x + ([''] * (max_length - len(x))) for x in target]return np.array(data), np.array(target)import tensorflow as tffrom helpers import lazy_propertyclass SequenceLabellingModel:def __init__(self, data, target, params): self.data = data self.target = target self.params = params self.prediction self.cost self.error self.optimize @lazy_propertydef length(self): used = tf.sign(tf.reduce_max(tf.abs(self.data), reduction_indices=2)) length = tf.reduce_sum(used, reduction_indices=1) length = tf.cast(length, tf.int32)return length @lazy_propertydef prediction(self): output, _ = tf.nn.dynamic_rnn( tf.nn.rnn_cell.GRUCell(self.params.rnn_hidden), self.data, dtype=tf.float32, sequence_length=self.length, )# Softmax layer.max_length = int(self.target.get_shape()[1]) num_classes = int(self.target.get_shape()[2]) weight = tf.Variable(tf.truncated_normal( [self.params.rnn_hidden, num_classes], stddev=0.01)) bias = tf.Variable(tf.constant(0.1, shape=[num_classes]))# Flatten to apply same weights to all time steps.output = tf.reshape(output, [-1, self.params.rnn_hidden]) prediction = tf.nn.softmax(tf.matmul(output, weight) + bias) prediction = tf.reshape(prediction, [-1, max_length, num_classes])return prediction @lazy_propertydef cost(self):# Compute cross entropy for each frame.cross_entropy = self.target * tf.log(self.prediction) cross_entropy = -tf.reduce_sum(cross_entropy, reduction_indices=2) mask = tf.sign(tf.reduce_max(tf.abs(self.target), reduction_indices=2)) cross_entropy *= mask# Average over actual sequence lengths.cross_entropy = tf.reduce_sum(cross_entropy, reduction_indices=1) cross_entropy /= tf.cast(self.length, tf.float32)return tf.reduce_mean(cross_entropy) @lazy_propertydef error(self): mistakes = tf.not_equal( tf.argmax(self.target, 2), tf.argmax(self.prediction, 2)) mistakes = tf.cast(mistakes, tf.float32) mask = tf.sign(tf.reduce_max(tf.abs(self.target), reduction_indices=2)) mistakes *= mask# Average over actual sequence lengths.mistakes = tf.reduce_sum(mistakes, reduction_indices=1) mistakes /= tf.cast(self.length, tf.float32)return tf.reduce_mean(mistakes) @lazy_propertydef optimize(self): gradient = self.params.optimizer.compute_gradients(self.cost)try: limit = self.params.gradient_clipping gradient = [ (tf.clip_by_value(g, -limit, limit), v)if g is not None else (None, v)for g, v in gradient]except AttributeError:print('No gradient clipping parameter specified.') optimize = self.params.optimizer.apply_gradients(gradient)return optimizeimport randomimport tensorflow as tfimport numpy as npfrom helpers import AttrDictfrom OcrDataset import OcrDatasetfrom SequenceLabellingModel import SequenceLabellingModelfrom batched import batched params = AttrDict( rnn_cell=tf.nn.rnn_cell.GRUCell, rnn_hidden=300, optimizer=tf.train.RMSPropOptimizer(0.002), gradient_clipping=5, batch_size=10, epochs=5, epoch_size=50)def get_dataset(): dataset = OcrDataset('./ocr')# Flatten images into vectors.dataset.data = dataset.data.reshape(dataset.data.shape[:2] + (-1,))# One-hot encode targets.target = np.zeros(dataset.target.shape + (26,))for index, letter in np.ndenumerate(dataset.target):if letter: target[index][ord(letter) - ord('a')] = 1dataset.target = target# Shuffle order of examples.order = np.random.permutation(len(dataset.data)) dataset.data = dataset.data[order] dataset.target = dataset.target[order]return dataset# Split into training and test data.dataset = get_dataset() split = int(0.66 * len(dataset.data)) train_data, test_data = dataset.data[:split], dataset.data[split:] train_target, test_target = dataset.target[:split], dataset.target[split:]# Compute graph._, length, image_size = train_data.shape num_classes = train_target.shape[2] data = tf.placeholder(tf.float32, [None, length, image_size]) target = tf.placeholder(tf.float32, [None, length, num_classes]) model = SequenceLabellingModel(data, target, params) batches = batched(train_data, train_target, params.batch_size) sess = tf.Session() sess.run(tf.initialize_all_variables())for index, batch in enumerate(batches): batch_data = batch[0] batch_target = batch[1] epoch = batch[2]if epoch >= params.epochs:breakfeed = {data: batch_data, target: batch_target} error, _ = sess.run([model.error, model.optimize], feed)print('{}: {:3.6f}%'.format(index + 1, 100 * error)) test_feed = {data: test_data, target: test_target} test_error, _ = sess.run([model.error, model.optimize], test_feed)print('Test error: {:3.6f}%'.format(100 * error))import tensorflow as tffrom helpers import lazy_propertyclass BidirectionalSequenceLabellingModel:def __init__(self, data, target, params): self.data = data self.target = target self.params = params self.prediction self.cost self.error self.optimize @lazy_propertydef length(self): used = tf.sign(tf.reduce_max(tf.abs(self.data), reduction_indices=2)) length = tf.reduce_sum(used, reduction_indices=1) length = tf.cast(length, tf.int32)return length @lazy_propertydef prediction(self): output = self._bidirectional_rnn(self.data, self.length) num_classes = int(self.target.get_shape()[2]) prediction = self._shared_softmax(output, num_classes)return predictiondef _bidirectional_rnn(self, data, length): length_64 = tf.cast(length, tf.int64) forward, _ = tf.nn.dynamic_rnn( cell=self.params.rnn_cell(self.params.rnn_hidden), inputs=data, dtype=tf.float32, sequence_length=length, scope='rnn-forward') backward, _ = tf.nn.dynamic_rnn( cell=self.params.rnn_cell(self.params.rnn_hidden), inputs=tf.reverse_sequence(data, length_64, seq_dim=1), dtype=tf.float32, sequence_length=self.length, scope='rnn-backward') backward = tf.reverse_sequence(backward, length_64, seq_dim=1) output = tf.concat(2, [forward, backward])return outputdef _shared_softmax(self, data, out_size): max_length = int(data.get_shape()[1]) in_size = int(data.get_shape()[2]) weight = tf.Variable(tf.truncated_normal( [in_size, out_size], stddev=0.01)) bias = tf.Variable(tf.constant(0.1, shape=[out_size]))# Flatten to apply same weights to all time steps.flat = tf.reshape(data, [-1, in_size]) output = tf.nn.softmax(tf.matmul(flat, weight) + bias) output = tf.reshape(output, [-1, max_length, out_size])return output @lazy_propertydef cost(self):# Compute cross entropy for each frame.cross_entropy = self.target * tf.log(self.prediction) cross_entropy = -tf.reduce_sum(cross_entropy, reduction_indices=2) mask = tf.sign(tf.reduce_max(tf.abs(self.target), reduction_indices=2)) cross_entropy *= mask# Average over actual sequence lengths.cross_entropy = tf.reduce_sum(cross_entropy, reduction_indices=1) cross_entropy /= tf.cast(self.length, tf.float32)return tf.reduce_mean(cross_entropy) @lazy_propertydef error(self): mistakes = tf.not_equal( tf.argmax(self.target, 2), tf.argmax(self.prediction, 2)) mistakes = tf.cast(mistakes, tf.float32) mask = tf.sign(tf.reduce_max(tf.abs(self.target), reduction_indices=2)) mistakes *= mask# Average over actual sequence lengths.mistakes = tf.reduce_sum(mistakes, reduction_indices=1) mistakes /= tf.cast(self.length, tf.float32)return tf.reduce_mean(mistakes) @lazy_propertydef optimize(self): gradient = self.params.optimizer.compute_gradients(self.cost)try: limit = self.params.gradient_clipping gradient = [ (tf.clip_by_value(g, -limit, limit), v)if g is not None else (None, v)for g, v in gradient]except AttributeError:print('No gradient clipping parameter specified.') optimize = self.params.optimizer.apply_gradients(gradient)return optimize
Referenzmaterial:
"TensorFlow Practice for Machine Intelligence"
Willkommen, mich auf WeChat hinzuzufügen, um zu kommunizieren: qingxingfengzi
Mein öffentliches WeChat-Konto: qingxingfengzigz
Das öffentliche WeChat-Konto meiner Frau Zhang Xingqing: qingqingfeifangz
Das obige ist der detaillierte Inhalt vonSequenzanmerkung, handgeschriebener OCR-Datensatz für Kleinbuchstaben, bidirektionales RNN. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!