Maison >développement back-end >Tutoriel Python >Annotation de séquence, ensemble de données OCR de lettres minuscules manuscrites, RNN bidirectionnel
Étiquetage de séquence, prédisant une catégorie pour chaque image de la séquence d'entrée. OCR (Optical Character Recognition, reconnaissance optique de caractères).
L'ensemble de données OCR (http://ai.stanford.edu/~btaskar/ocr/) collecté par Rob Kassel du Spoken Language System Research Group du MIT et prétraité par Ben Taskar de l'Université de Stanford Le Laboratoire d'Intelligence Artificielle contient un grand nombre d'écritures manuscrites individuelles, chaque échantillon correspond à une image binaire de 16X8 pixels. Les lignes de mots combinent des séquences et les séquences correspondent aux mots. 6 800 mots de 14 lettres maximum. gzip compressé, le contenu est un fichier texte délimité par des tabulations. Le module Python csv lit directement. Chaque ligne du fichier possède un attribut de lettre normalisé, un numéro d'identification, une étiquette, une valeur de pixel, un numéro d'identification de la lettre suivante, etc.
Triez par la valeur d'identification de la lettre suivante et lisez chaque lettre de mot dans le bon ordre. Les lettres sont collectées jusqu'à ce que le prochain champ correspondant à l'ID ne soit pas défini. Lire la nouvelle séquence. Après avoir lu les lettres cibles et les pixels de données, remplissez l'objet séquence avec zéro image, qui peut être incluse dans le tableau NumPy de toutes les données de pixels des deux plus grandes lettres cibles.
La couche softmax est partagée entre les pas de temps. Les tableaux de données et cibles contiennent des séquences, une image pour chaque lettre cible. Extension RNN, ajoutant un classificateur softmax à chaque sortie de lettre. Le classificateur évalue les prédictions sur chaque trame de données plutôt que sur la séquence entière. Calculez la longueur de la séquence. Une couche softmax est ajoutée à toutes les images : soit plusieurs classificateurs différents sont ajoutés à toutes les images, soit toutes les images partagent le même classificateur. Avec un classificateur partagé, les poids sont ajustés plusieurs fois pendant l'entraînement, pour chaque lettre du mot d'entraînement. Une dimension de matrice de poids de couche entièrement connectée batch_size*in_size*out_size. Il est maintenant nécessaire de mettre à jour la matrice de poids dans les deux dimensions d'entrée batch_size et séquence_steps. Laissez l'entrée (valeur d'activité de sortie RNN) être aplatie dans la forme batch_size*sequence_steps*in_size. La matrice de poids devient un plus grand lot de données. Le résultat n'est pas aplati.
Fonction de coût, chaque image de la séquence a une paire cible prédite, moyennée dans la dimension correspondante. tf.reduce_mean normalisé en fonction de la longueur du tenseur (longueur maximale de la séquence) ne peut pas être utilisé. Il est nécessaire de normaliser en fonction de la longueur réelle de la séquence et d'appeler manuellement tf.reduce_sum et la moyenne de l'opération de division.
Fonction de perte, tf.argmax est pour l'axe 2 et non pour l'axe 1, chaque image est remplie et la moyenne est calculée en fonction de la longueur réelle de la séquence. tf.reduce_mean prend la moyenne de tous les mots dans les données par lots.
Le calcul dérivé automatique de TensorFlow peut utiliser la même opération d'optimisation pour la classification des séquences et n'a besoin que de remplacer une nouvelle fonction de coût. Coupez tous les gradients RNN pour éviter les divergences d’entraînement et éviter les effets négatifs.
Formez le modèle, get_sataset télécharge l'image d'écriture manuscrite, le prétraitement, le vecteur d'encodage à chaud des lettres minuscules. Perturbez aléatoirement l'ordre des données et divisez-les en ensembles d'apprentissage et en ensembles de test.
Il existe une relation de dépendance (ou information mutuelle) entre les lettres adjacentes d'un mot, et RNN enregistre toutes les informations d'entrée du même mot dans la valeur d'activité implicite. Pour la classification des premières lettres, le réseau ne dispose pas d’une grande quantité d’entrées pour déduire des informations supplémentaires, et le RNN bidirectionnel surmonte les lacunes.
Deux RNN observent la séquence d'entrée, l'un lit les mots de l'extrémité gauche dans l'ordre habituel et l'autre lit les mots de l'extrémité droite dans l'ordre inverse. Deux valeurs d'activité de sortie sont obtenues pour chaque pas de temps. Épissage avant envoi à la couche softmax partagée. Le classificateur obtient des informations complètes sur les mots de chaque lettre. tf.modle.rnn.bidirectionnel_rnn est implémenté.
Implémentez le RNN bidirectionnel. Divisez les attributs prédictifs en deux fonctions et concentrez-vous sur moins de contenu. La fonction _shared_softmax transmet les données du tenseur de fonction pour déduire la taille d'entrée. En réutilisant d'autres fonctions architecturales, la même technique d'aplatissement partage la même couche softmax à tous les pas de temps. rnn.dynamic_rnn crée deux RNN.
L'inversion de séquence est plus facile que la mise en œuvre de la nouvelle opération RNN de passe inversée. La fonction tf.reverse_sequence inverse les trames séquence_lengths dans les données de trame. Les nœuds du diagramme de flux de données ont des noms. Le paramètre scope est le nom de portée de la variable rnn_dynamic_cell et la valeur par défaut est RNN. Les deux paramètres sont différents pour RNN et nécessitent des domaines différents.
La séquence inversée est introduite dans le RNN arrière, et la sortie réseau est inversée et alignée avec la sortie avant. Concaténez deux tenseurs le long de la dimension de sortie et du retour du neurone RNN. Le modèle RNN bidirectionnel est plus performant.
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
Matériel de référence :
"TensorFlow Practice for Machine Intelligence"
Bienvenue pour m'ajouter sur WeChat pour communiquer : qingxingfengzi
Mon compte public WeChat : qingxingfengzigz
Le compte public WeChat de ma femme Zhang Xingqing : qingqingfeifangz
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!