tensorflowの道-元のテキストデータをどのように処理するか

10783 ワード

このシリーズを書く初志は、tensorflowに関するチュートリアルが少なすぎて、歪果仁が書いたものもあるということです.例えば、TensorFlow-Examples tensorflw_tutorials TensorFlow-Tutorials Tensorflow-101個人はこれらのチュートリアルが初心者にとって説明が細かくないと感じて、ほとんど作者がコードを書いてipython notebookに置いて、みんなは地元のrun 1 runにダウンロードして、とても喜んで結果を得て、実際には何がこのように構築して、すべてのステップがどのような結果を得るのか分かりません.あるいは自分でこれらの牛人のコードを理解したいと思っていますが、公式のapiドキュメントは入門にとって友好的ではなく、ドキュメントを見てもよくわかりません.この時、誰かが指導してくれることを望んでいます.そこで「ハンドル&敷居ゼロのtensorflow中国語チュートリアル」を書く考えが芽生えました.もっと多くの人がdeep learningとtensorflowを理解することができることを望んで、みんなは多く意見を出して、多く交流します!今日解読するコードはやはりCNNに基づいてテキスト分類を実現するか、この問題の重要な一歩は原始データの読み取りと前処理であり、詳細コードは(1)load data and labels実験で使用したデータが腐ったトマトのmoview reviewsであることを参照し、まず提供されたデータがどのようなsorryであるかを見て、画像の欠落は見ることができ、各行はreviewであり、データは初歩的な処理を行ったことがある.しかし、「doesn’t/it’s」と同様に分割は行われていない.後でこの問題について話します.
def load_data_and_labels():
    """
    Loads MR polarity data from files, splits the data into words and generates labels.
    Returns split sentences and labels.
    """
    # Load data from files
    positive_examples = list(open("./data/rt-polaritydata/rt-polarity.pos", "r").readlines())
    positive_examples = [s.strip() for s in positive_examples]
    negative_examples = list(open("./data/rt-polaritydata/rt-polarity.neg", "r").readlines())
    negative_examples = [s.strip() for s in negative_examples]
    # Split by words
    x_text = positive_examples + negative_examples
    x_text = [clean_str(sent) for sent in x_text]
    x_text = [s.split(" ") for s in x_text]
    # Generate labels
    positive_labels = [[0, 1] for _ in positive_examples]
    negative_labels = [[1, 0] for _ in negative_examples]
    y = np.concatenate([positive_labels, negative_labels], 0)
    return [x_text, y]

この関数は、positiveとnegativeのデータをファイルからロードし、それらを組み合わせて文ごとに分詞する役割を果たすため、x_textは2次元リストで、reviewごとのwordを格納します.これらの対応するlabelsも組み合わせられ、labelsは実際には2分類出力層の2つのニューロンに対応するため、one−hotで0/1と1/0に符号化され、yに戻る.ここで、f.readlines()の戻り値はlistであり、各要素は1行のテキスト(strタイプ、末尾に「」)であるため、外層でlist()に変換してs.strip()関数で各sentence末尾の改行文字と空白文字を削除する必要はありません.改行を除いた後、先ほど述べた問題のため、各sentenceは、clean_str()関数に具体的には、句読点や略語などを分割する操作を行う必要があります.英語strの最も簡潔な分詞方式はスペースsplitであるため、分割する必要がある部位ごとにスペースを加え、str全体にsplit(")関数を呼び出すだけで分詞を完了することができる.labelsの生成も同様である.
(2) padding sentence
def pad_sentences(sentences, padding_word=""):
    """
    Pads all sentences to the same length. The length is defined by the longest sentence.
    Returns padded sentences.
    """
    sequence_length = max(len(x) for x in sentences)
    padded_sentences = []
    for i in range(len(sentences)):
        sentence = sentences[i]
        num_padding = sequence_length - len(sentence)
        new_sentence = sentence + [padding_word] * num_padding
        padded_sentences.append(new_sentence)
    return padded_sentences

なぜsentenceをpaddingするのですか?TextCNNモデルのinput_xはtfに対応する.placeholderは、tensorです.shapeは固定されています.例えば、[batch,sequence_len]では、tensorの各行に異なる長さを持つことはできません.そのため、dataset全体で最も長いsentenceの長さを見つけ、長さに満たない文の末尾にpadding wordsを加えて、input sentenceの長さが一致することを保証する必要があります.
load_でData関数では、各sentenceデータを格納する2次元リストが得られるのでpadding_sentencesの後も、このような形で戻ります.ただし、各文リストの末尾にpadding wordが追加されている可能性があります.
(3) build vocabulary
def build_vocab(sentences):
    """
    Builds a vocabulary mapping from word to index based on the sentences.
    Returns vocabulary mapping and inverse vocabulary mapping.
    """
    # Build vocabulary
    word_counts = Counter(itertools.chain(*sentences))
    # Mapping from index to word
    vocabulary_inv = [x[0] for x in word_counts.most_common()]
    vocabulary_inv = list(sorted(vocabulary_inv))
    # Mapping from word to index
    vocabulary = {x: i for i, x in enumerate(vocabulary_inv)}
    return [vocabulary, vocabulary_inv]

collectionsモジュールのCounterは、次のような語周波数の統計を実現できることを知っています.
import collections
sentence = ["i", "love", "mom", "mom", "loves", "me"]
collections.Counter(sentence)
>>> Counter({'i': 1, 'love': 1, 'loves': 1, 'me': 1, 'mom': 2})

Counterが受け入れるパラメータはiterableですが、現在は複数の文のリストがあり、複数のsentence wordリストのすべてのwordを効率的な反復器で生成するにはどうすればいいですか?これはitertoolsです.chain(*iterables)、具体的な使い方はこちらを参考に
複数の反復器をパラメータとするが、単一の反復器のみを返し、単一のシーケンスから来たようにすべてのパラメータ反復器の内容を生成する.
これにより、データセット全体の語周波数統計、word_counts. 辞書vocabularyを作成するにはword_countsでは各pairの最初の要素であるword(Counterがここで重み付けをした仕事に相当)を抽出し,語周波数に基づいてvocabularyを構築する必要がなく,wordの辞書順に基づいているのでvocabularyに対してsortedを行い,辞書順のword listを得た.頭文字が小さいのが前に並んでいます.さらにdictを確立し、wordごとに対応するindex、すなわちvocabulary変数を格納します.
(4) build input data
def build_input_data(sentences, labels, vocabulary):
    """
    Maps sentencs and labels to vectors based on a vocabulary.
    """
    x = np.array([[vocabulary[word] for word in sentence] for sentence in sentences])
    y = np.array(labels)
    return [x, y]

上の2つの関数から,すべてのsentences分詞後の2次元リスト,sentences対応labels,および各word対応indexをクエリーするvocabulary辞書を得た.でも!考えてみれば、現在のsentencesにはword文字列が1つずつ格納されており、データ量が大きい場合はメモリを占めるため、word対応のindex、indexはintであり、占有空間が小さいことが望ましい.そこで生成したばかりのvocabularyを用いてsentencesの2次元リストのwordごとにクエリーを行い,word indexからなる2次元リストを生成する.最後に、この2次元リストをnumpyの2次元arrayに変換します.対応するlablesはすでに0,1の2次元リストなのでarrayに直接変換できます.arrayに移行すると、直接cnnのinputやlabelsとして使用できます.
(5) load data
def load_data():
    """
    Loads and preprocessed data for the MR dataset.
    Returns input vectors, labels, vocabulary, and inverse vocabulary.
    """
    # Load and preprocess data
    sentences, labels = load_data_and_labels()
    sentences_padded = pad_sentences(sentences)
    vocabulary, vocabulary_inv = build_vocab(sentences_padded)
    x, y = build_input_data(sentences_padded, labels, vocabulary)
    return [x, y, vocabulary, vocabulary_inv]

最後に上の各部分の処理関数を統合し、
1.最初にテキストファイルから元のデータをロードし、最初にリストにsentence形式で一時保存し、次に各sentenceに対してclean_を行うstr,分詞,wordを基本単位とする2次元リストsentencesを得る,labelsは[0,1]と[1,0]に対応する.sentenceの最大長を見つけて、長さが足りない文に対してpadding 3を行います.データに基づいて語彙を作成し、辞書順に返し、wordごとに対応するindexを得る.4.strタイプの2 Dリストsentencesをintタイプのsentencesに変換し、モデルのinputおよびlabelsとして2 D numpy arrayを返して後で使用します.(6) generate batch
def batch_iter(data, batch_size, num_epochs, shuffle=True):
    """
    Generates a batch iterator for a dataset.
    """
    data = np.array(data)
    data_size = len(data)
    num_batches_per_epoch = int(len(data)/batch_size) + 1
    for epoch in range(num_epochs):
        # Shuffle the data at each epoch
        if shuffle:
            shuffle_indices = np.random.permutation(np.arange(data_size))
            shuffled_data = data[shuffle_indices]
        else:
            shuffled_data = data
        for batch_num in range(num_batches_per_epoch):
            start_index = batch_num * batch_size
            end_index = min((batch_num + 1) * batch_size, data_size)
            yield shuffled_data[start_index:end_index]

この関数の役割は,訓練全体にわたってbatches=batch_を定義することである.iter(...)、トレーニング中はforサイクルというbatchesだけで各batchデータを操作できます.