TensorFlowで入力データの長さが可変なLSTMを実装する

以下の記事の続き k17trpsynth.hatenablog.com

目的

前回作ったLSTMは入力する文字列の長さをあらかじめ指定して学習し、予測する際も入力データの長さをそれに合わせるというもので、入力データの長さは不変であった。今回は予測時の入力データの長さが可変なLSTMを作りたい。

方法

利用した文字データなど、前回同様。
static_rnn(入力文字数が静的なRNN)をdynamic_rnn(入力文字数が動的なRNN)に変える。

hidden_to_output, final_state = tf.nn.dynamic_rnn(multi_cell, input_to_hidden, initial_state=initial_state, time_major=True)

この際、cell層に与えるデータは「入力文字数」×「バッチサイズ」×「隠れ層のユニット数」のサイズのTensorでなければならない。cellに渡す前に以下のようにインプットデータを整形する。

input = tf.reshape(tf.transpose(input, [1, 0, 2]), [-1, self.input_layer_size])
input_to_hidden = (tf.matmul(input, w_hidden) + b_hidden)
input_to_hidden = tf.reshape(input_to_hidden, [self.max_word_length, -1, self.hidden_layer_size])

また、LSTMを用いる際、cellの初期状態は「cellの初期状態」と「参照する直前の出力の初期状態」をタプルで繋げる必要があった。static_rnnでは通常のタプルで繋げるだけで良かったが、dynamic_rnnでそれをすると以下のようなエラーがでる。

TypeError: The two structures don't have the same sequence type. First structure has type <class 'tuple'>, while second structure has type <class 'tensorflow.python.ops.rnn_cell_impl.LSTMStateTuple'>.

dynamic_rnnでは通常のタプルで繋げるのではなく、以下のようにtf.contrib.rnn.LSTMStateTupleで繋げる必要がある。

initial_state_c = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
initial_state_h = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
initial_state = tuple([tf.contrib.rnn.LSTMStateTuple(initial_state_c, initial_state_h)] * self.num_hidden_layer)

コードの全容は以下の通り。

import tensorflow as tf
import numpy as np
import re



class Prepare_data:


    def __init__(self, file):
        self.max_word_length = 10
        self.vocabulary_size = 27
        self.file = open(file)


    def file_to_text(self):
        text = self.file.read()
        return text


    def text_to_array(self):
        text = self.file_to_text()
        text = text.lower()
        text = text.replace("\n", " ")
        text = re.sub(r"[^a-z ]", "", text)
        text = re.sub(r"[ ]+", " ", text)

        code_list = []
        for i in range(len(text)):
            if text[i] == " ":
                code_list.append(self.vocabulary_size - 1)
            else:
                code_list.append(ord(text[i])-ord("a"))
        code_array = np.array(code_list)
        return code_array


    def array_to_one_hot(self):
        array = self.text_to_array()
        one_hot = np.eye(self.vocabulary_size)[array]
        return one_hot


    def make_data(self):
        one_hot = self.array_to_one_hot()
        data_num = one_hot.shape[0] - self.max_word_length
        input_data = np.zeros([data_num, self.max_word_length, self.vocabulary_size])
        output_data = np.zeros([data_num, self.vocabulary_size])
        for i in range(data_num):
            output_data[i, :] = one_hot[i + self.max_word_length, :]
            for j in range(self.max_word_length):
                input_data[i, j, :] = one_hot[i + j, :]
        training_num = data_num * 4 // 5
        input_train = input_data[: training_num]
        output_train = output_data[: training_num]
        input_test = input_data[training_num :]
        output_test = output_data[training_num :]
        return input_train, output_train, input_test, output_test



class Lstm:


    def __init__(self):
        self.input_layer_size = 27
        self.hidden_layer_size = 30
        self.num_hidden_layer = 1
        self.output_layer_size = 27
        self.max_word_length = 10
        self.batch_size = 128
        self.epoch = 100


    def inference(self, input, initial_state):
        w_hidden = tf.Variable(tf.random_normal([self.input_layer_size, self.hidden_layer_size], dtype="float64"))
        b_hidden = tf.Variable(tf.random_normal([self.hidden_layer_size], dtype="float64"))
        w_output = tf.Variable(tf.random_normal([self.hidden_layer_size, self.output_layer_size], dtype="float64"))
        b_output = tf.Variable(tf.random_normal([self.output_layer_size], dtype="float64"))

        input = tf.reshape(tf.transpose(input, [1, 0, 2]), [-1, self.input_layer_size])
        input_to_hidden = (tf.matmul(input, w_hidden) + b_hidden)
        input_to_hidden = tf.reshape(input_to_hidden, [self.max_word_length, -1, self.hidden_layer_size])

        cell = tf.nn.rnn_cell.BasicLSTMCell(self.hidden_layer_size, reuse=tf.AUTO_REUSE)
        multi_cell = tf.nn.rnn_cell.MultiRNNCell([cell] * self.num_hidden_layer)
        hidden_to_output, final_state = tf.nn.dynamic_rnn(multi_cell, input_to_hidden, initial_state=initial_state, time_major=True)

        output = (tf.matmul(hidden_to_output[-1], w_output) + b_output)

        return output


    def cost(self, output, labels):
        cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=output))
        return cost


    def training(self, cost):
        optimizer = tf.train.AdamOptimizer()
        training = optimizer.minimize(cost)
        return training


    def train(self, file):
        test_data = Prepare_data(file)
        input_train, output_train, input_test, output_test = test_data.make_data()
        zero_state = np.zeros([self.batch_size, self.hidden_layer_size], dtype="float64")

        input_data = tf.placeholder(tf.float64, [None, self.max_word_length, self.input_layer_size])
        labels = tf.placeholder(tf.float64, [None, self.output_layer_size])
        initial_state_c = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
        initial_state_h = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
        initial_state = tuple([tf.contrib.rnn.LSTMStateTuple(initial_state_c, initial_state_h)] * self.num_hidden_layer)

        prediction = self.inference(input_data, initial_state)
        cost = self.cost(prediction, labels)
        training = self.training(cost)
        correct = tf.equal(tf.argmax(prediction, 1), tf.argmax(labels, 1))
        accuracy = tf.reduce_mean(tf.cast(correct, dtype="float64"))

        with tf.Session() as sess:
            init = tf.global_variables_initializer()
            sess.run(init)

            for epoch in range(self.epoch):
                step = 1
                sum_cost = 0
                sum_acc = 0

                while self.batch_size * step < input_train.shape[0]:
                    input_batch = input_train[self.batch_size * (step - 1) : self.batch_size * step]
                    output_batch = output_train[self.batch_size * (step - 1) : self.batch_size * step]
                    c, _, a= sess.run([cost, training, accuracy], feed_dict = {input_data: input_batch, labels: output_batch, initial_state_c: zero_state, initial_state_h: zero_state})
                    sum_cost += c
                    sum_acc += a
                    step += 1

                ave_cost = sum_cost / step
                epoch_acc = sum_acc / step
                print("epoch: {0}, cost: {1}, epoch_accuracy: {2}".format(epoch, ave_cost, epoch_acc))

            print("Training finished")

            saver = tf.train.Saver()
            saver.save(sess, "./lstm_model")

            zero_state = np.zeros([input_test.shape[0], self.hidden_layer_size], dtype = "float64")

            a = sess.run(accuracy, feed_dict = {input_data: input_test, labels: output_test, initial_state_c: zero_state, initial_state_h: zero_state})
            print("accuracy: {0}".format(a))


    def predict(self, context):
        context = context.replace("\n", " ")
        context = re.sub(r"[^a-z ]", "", context)
        context = re.sub(r"[ ]+", " ", context)

        code_list = []
        for i in range(len(context)):
            if context[i] == " ":
                code_list.append(self.input_layer_size - 1)
            else:
                code_list.append(ord(context[i])-ord("a"))
        code_array = np.array(code_list)
        one_hot = np.eye(self.input_layer_size)[code_array]
        input_pred = np.array([one_hot])

        zero_state = np.zeros([1, self.hidden_layer_size], dtype="float64")

        input_data = tf.placeholder(tf.float64, [None, self.max_word_length, self.input_layer_size])
        initial_state_c = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
        initial_state_h = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
        initial_state = tuple([tf.contrib.rnn.LSTMStateTuple(initial_state_c, initial_state_h)] * self.num_hidden_layer)

        prediction = tf.nn.softmax(self.inference(input_pred, initial_state))
        labels_pred = tf.argmax(prediction, 1)

        with tf.Session() as sess:
            saver = tf.train.Saver()
            saver.restore(sess, "./lstm_model")

            p, l = sess.run([prediction, labels_pred], feed_dict = {input_data: input_pred, initial_state_c: zero_state, initial_state_h: zero_state})

            for i in range(27):
                c = "_" if i == 26 else chr(i + ord("a"))
                print("{0}: {1}%".format(c, int(p[0][i] * 10000) / 100))

            print("prediction: {0}{1}".format(context, "_" if l[0] == 26 else chr(l[0] + ord("a"))))




if __name__=="__main__":
    test_lstm = Lstm()
    test_lstm.train("make_data.txt")
    test_lstm.predict("convenienc")

TensorFlowでLSTMを実装する

以下の記事の続き k17trpsynth.hatenablog.com

目的

LSTMを使って前回作ったRNNを改良したい。加えて、隠れ層の数を複数にしたディープリカレントニューラルネットワークを構築することにも試みた。

方法

利用した文字データなど、前回同様。

まず、前回のBasicRNNCellをBasicLSTMCellに変更する。

cell = tf.nn.rnn_cell.BasicLSTMCell(self.hidden_layer_size, reuse=tf.AUTO_REUSE)

加えて、initial_stateに手を加える必要がある。2つの初期値をタプルで繋げて用意した。

initial_state_c = tf.placeholder(tf.float64, [None, self.hidden_layer_size])    #cellの状態の初期値
initial_state_h = tf.placeholder(tf.float64, [None, self.hidden_layer_size])    #前のcellの出力を参照するのでその初期値
initial_state = (initial_state_c, initial_state_h)    #2つの初期値をタプルで繋げる

これは、LSTMのcellが入力と出力を繰り返す中で1つ前のcellの出力を参照するため、その初期値(ここでのinitial_state_h)をcellの状態(state)と合わせて定義していると考えられる。

また今回隠れ層の数を複数にすることも試みた。tf.nn.rnn_cell.MultiRNNCellを用いて以下のように記述する。

multi_cell = tf.nn.rnn_cell.MultiRNNCell([cell] * self.num_hidden_layer)    #num_hidden_layer: 隠れ層の数

この際も、cellの数だけその初期値が必要となるため、initial_stateに手を加える必要があり、隠れ層の数だけinitial_stateをタプルで繋げて用意する必要がある。

initial_state_c = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
initial_state_h = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
initial_state = tuple([(initial_state_c, initial_state_h)] * self.num_hidden_layer)    #リストで連結したLSTMCellの初期値をタプルに変換

コードの全容は以下の通り。上で述べた部分以外は前回同様である。

import tensorflow as tf
import numpy as np
import re



class Prepare_data:


    def __init__(self, file):
        self.chunk_size = 5    #参照する直前の文脈の文字数
        self.vocabulary_size = 27    #アルファベット26字 + インデント
        self.file = open(file)


    def file_to_text(self):
        text = self.file.read()
        return text


    def text_to_array(self):
        text = self.file_to_text()
        text = text.lower()
        text = text.replace("\n", " ")    #改行をインデントに変換
        text = re.sub(r"[^a-z ]", "", text)
        text = re.sub(r"[ ]+", " ", text)

        code_list = []
        for i in range(len(text)):
            if text[i] == " ":
                code_list.append(self.vocabulary_size - 1)
            else:
                code_list.append(ord(text[i])-ord("a"))
        code_array = np.array(code_list)    #アルファベットとインデントを0~26の数字に変換した配列を作成
        return code_array


    def array_to_one_hot(self):
        array = self.text_to_array()
        one_hot = np.eye(self.vocabulary_size)[array]    #数字の配列をone-hotベクトルの配列に変換
        return one_hot


    def make_data(self):
        one_hot = self.array_to_one_hot()
        data_num = one_hot.shape[0] - self.chunk_size    #文の最初の方はデータとして利用できない
        input_data = np.zeros([data_num, self.chunk_size, self.vocabulary_size])
        output_data = np.zeros([data_num, self.vocabulary_size])
        for i in range(data_num):
            output_data[i, :] = one_hot[i + self.chunk_size, :]
            for j in range(self.chunk_size):
                input_data[i, j, :] = one_hot[i + j, :]
        training_num = data_num * 4 // 5
        input_train = input_data[: training_num]
        output_train = output_data[: training_num]
        input_test = input_data[training_num :]
        output_test = output_data[training_num :]
        return input_train, output_train, input_test, output_test



class Lstm:


    def __init__(self):
        self.input_layer_size = 27
        self.hidden_layer_size = 30
        self.num_hidden_layer = 1
        self.output_layer_size = 27
        self.chunk_size = 5
        self.batch_size = 128
        self.epoch = 100


    def inference(self, input, initial_state):
        w_hidden = tf.Variable(tf.random_normal([self.input_layer_size, self.hidden_layer_size], dtype="float64"))
        b_hidden = tf.Variable(tf.random_normal([self.hidden_layer_size], dtype="float64"))
        w_output = tf.Variable(tf.random_normal([self.hidden_layer_size, self.output_layer_size], dtype="float64"))
        b_output = tf.Variable(tf.random_normal([self.output_layer_size], dtype="float64"))

        input = tf.reshape(tf.transpose(input, [1, 0, 2]), [-1, self.input_layer_size])    #インプットデータをwとbをかけるために整形
        input_to_hidden = (tf.matmul(input, w_hidden) + b_hidden)
        input_to_hidden = tf.split(input_to_hidden, self.chunk_size)    #chunk_sizeだけ連なったリストを作成

        cell = tf.nn.rnn_cell.BasicLSTMCell(self.hidden_layer_size, reuse=tf.AUTO_REUSE)    #cellの定義
        multi_cell = tf.nn.rnn_cell.MultiRNNCell([cell] * self.num_hidden_layer)    #隠れ層を複製
        hidden_to_output, final_state = tf.contrib.rnn.static_rnn(multi_cell, input_to_hidden, initial_state=initial_state)

        output = (tf.matmul(hidden_to_output[-1], w_output) + b_output)    #chunk_size個のcell出力のうち最後のみを利用

        return output


    def cost(self, output, labels):
        cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=output))
        return cost


    def training(self, cost):
        optimizer = tf.train.AdamOptimizer()
        training = optimizer.minimize(cost)
        return training


    def train(self, file):
        test_data = Prepare_data(file)
        input_train, output_train, input_test, output_test = test_data.make_data()    #引数fileからデータを作成
        zero_state = np.zeros([self.batch_size, self.hidden_layer_size], dtype="float64")    #cellの状態の初期値を定義

        input_data = tf.placeholder(tf.float64, [None, self.chunk_size, self.input_layer_size])
        labels = tf.placeholder(tf.float64, [None, self.output_layer_size])
        initial_state_c = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
        initial_state_h = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
        initial_state = tuple([(initial_state_c, initial_state_h)] * self.num_hidden_layer)

        prediction = self.inference(input_data, initial_state)
        cost = self.cost(prediction, labels)
        training = self.training(cost)
        correct = tf.equal(tf.argmax(prediction, 1), tf.argmax(labels, 1))
        accuracy = tf.reduce_mean(tf.cast(correct, dtype="float64"))

        with tf.Session() as sess:
            init = tf.global_variables_initializer()
            sess.run(init)

            for epoch in range(self.epoch):
                step = 1
                sum_cost = 0
                sum_acc = 0

                while self.batch_size * step < input_train.shape[0]:
                    input_batch = input_train[self.batch_size * (step - 1) : self.batch_size * step]
                    output_batch = output_train[self.batch_size * (step - 1) : self.batch_size * step]
                    c, _, a= sess.run([cost, training, accuracy], feed_dict = {input_data: input_batch, labels: output_batch, initial_state_c: zero_state, initial_state_h: zero_state})
                    sum_cost += c
                    sum_acc += a
                    step += 1

                ave_cost = sum_cost / step
                epoch_acc = sum_acc / step
                print("epoch: {0}, cost: {1}, epoch_accuracy: {2}".format(epoch, ave_cost, epoch_acc))

            print("Training finished")

            saver = tf.train.Saver()
            saver.save(sess, "./lstm_model")    #モデルの変数の保存

            zero_state = np.zeros([input_test.shape[0], self.hidden_layer_size], dtype = "float64")    #cellの状態の初期値を再定義

            a = sess.run(accuracy, feed_dict = {input_data: input_test, labels: output_test, initial_state_c: zero_state, initial_state_h: zero_state})
            print("accuracy: {0}".format(a))


    def predict(self, context):
        #訓練データ同様、文脈データの整形
        context = context.replace("\n", " ")
        context = re.sub(r"[^a-z ]", "", context)
        context = re.sub(r"[ ]+", " ", context)

        code_list = []
        for i in range(self.chunk_size):
            if context[- self.chunk_size + i] == " ":
                code_list.append(self.input_layer_size - 1)
            else:
                code_list.append(ord(context[- self.chunk_size + i])-ord("a"))
        code_array = np.array(code_list)
        one_hot = np.eye(self.input_layer_size)[code_array]
        input_pred = np.array([one_hot])

        zero_state = np.zeros([1, self.hidden_layer_size], dtype="float64")    #cellの状態の初期値を再定義

        input_data = tf.placeholder(tf.float64, [None, self.chunk_size, self.input_layer_size])
        initial_state_c = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
        initial_state_h = tf.placeholder(tf.float64, [None, self.hidden_layer_size])
        initial_state = tuple([(initial_state_c, initial_state_h)] * self.num_hidden_layer)

        prediction = tf.nn.softmax(self.inference(input_pred, initial_state))
        labels_pred = tf.argmax(prediction, 1)

        with tf.Session() as sess:
            saver = tf.train.Saver()
            saver.restore(sess, "./lstm_model")    #保存したモデルの変数をロード

            p, l = sess.run([prediction, labels_pred], feed_dict = {input_data: input_pred, initial_state_c: zero_state, initial_state_h: zero_state})

            for i in range(27):
                c = "_" if i == 26 else chr(i + ord("a"))
                print("{0}: {1}".format(c, int(p[0][i] * 10000) / 100))    #文字ごとの確率を表示

            print("prediction: {0}{1}".format(context, "_" if l[0] == 26 else chr(l[0] + ord("a"))))    #最終的な予測を表示




if __name__=="__main__":
    test_lstm = Lstm()
    test_lstm.train("make_data.txt")
    test_lstm.predict("convenienc")

隠れ層の数=1とした時でも正答率は5割程度となり、前回の正答率4~4.5割から改善が見られた。隠れ層の数を増やしてもあまり正答率の変化は見られなかった。
また、参照する文字数(chunk_size)を5から変化させてみたが、正答率は特に変化せず、5割程度だった。

ただ、このコードでは一度利用したcellが再利用できない。再利用しようとすると以下のエラーがでる。

NotFoundError (see above for traceback): Key Variable_4 not found in checkpoint
     [[Node: save_1/RestoreV2_4 = RestoreV2[dtypes=[DT_DOUBLE], _device="/job:localhost/replica:0/task:0/device:CPU:0"](_arg_save_1/Const_0_0, save_1/RestoreV2_4/tensor_names, save_1/RestoreV2_4/shape_and_slices)]]

このVariable_4はcell内の変数を表すと考えられ、なぜか再利用時には無くなっている。原因は不明。

追記(2018.2.27)

解決しました。

k17trpsynth.hatenablog.com

TensorFlowでRNNを実装する

目的

文脈から次に来る文字を予測するRNNを作る。
具体的には連続する英字5文字をインプットデータとして与えた時に次に来る英字1文字をアウトプットするものを作りたい。

方法

TensorFlowを用いる。

RNNへ与える英字データは以下のリンクの記事本文の英字をone_hotベクトル形式にしたものを利用した。そのデータ整形のコードは後述する。
‘An Endless War’: Why 4 U.S. Soldiers Died in a Remote African Desert - The New York Times

RNNの構造を記述したコードは以下の通り。

def __init__(self):
    self.input_layer_size = 27
    self.hidden_layer_size = 30
    self.output_layer_size = 27
    self.chunk_size = 5
    self.batch_size = 128
    self.epoch = 100


def inference(self, input, initial_state):
    w_hidden = tf.Variable(tf.random_normal([self.input_layer_size, self.hidden_layer_size], dtype="float64"))
    b_hidden = tf.Variable(tf.random_normal([self.hidden_layer_size], dtype="float64"))
    w_output = tf.Variable(tf.random_normal([self.hidden_layer_size, self.output_layer_size], dtype="float64"))
    b_output = tf.Variable(tf.random_normal([self.output_layer_size], dtype="float64"))

    input = tf.reshape(tf.transpose(input, [1, 0, 2]), [-1, self.input_layer_size])
    input_to_hidden = (tf.matmul(input, w_hidden) + b_hidden)
    input_to_hidden = tf.split(input_to_hidden, self.chunk_size)

    cell = tf.nn.rnn_cell.BasicRNNCell(self.hidden_layer_size, tf.AUTO_REUSE)
    hidden_to_output, final_state = tf.contrib.rnn.static_rnn(cell, input_to_hidden, initial_state=initial_state)

    output = (tf.matmul(hidden_to_output[-1], w_output) + b_output)

    return output

以下に順に解説していく。

def __init__(self):
    self.input_layer_size = 27
    self.hidden_layer_size = 30
    self.output_layer_size = 27
    self.chunk_size = 5
    self.batch_size = 128
    self.epoch = 1000

input_layer_sizeとoutput_layer_sizeは英字の種類であり、27個(アルファベット26文字+インデント)。chunk_sizeは何文字前まで参照するかを定義し、今回は5文字。

inferenceメソッドはRNNの構造を記したもの。重み(w)とバイアス(b)は通常のDNNと同様。通常のDNN少し異なるのは以下の部分。

input = tf.reshape(tf.transpose(input, [1, 0, 2]), [-1, self.input_layer_size])
input_to_hidden = (tf.matmul(input, w_hidden) + b_hidden)
input_to_hidden = tf.split(input_to_hidden, self.chunk_size)

cell = tf.nn.rnn_cell.BasicRNNCell(self.hidden_layer_size, tf.AUTO_REUSE)
hidden_to_output, final_state = tf.contrib.rnn.static_rnn(cell, input_to_hidden, initial_state=self.initial_state)

output = (tf.matmul(hidden_to_output[-1], w_output) + b_output)

RNNは隠れ層に繰り返しinputを渡し続けるNNであるためinputはデータがchunk_size(5個)だけ繋がったリストとなる。
隠れ層はデータを受け取るたびに出力層へデータを渡す。今回欲しい出力データはこのうちの最後(5個目)のデータである。なぜなら、RNNは隠れ層のcellと呼ばれる部分に過去の入力データを記憶する性質を持つため、最後に出力されるデータは過去全ての入力データを参照したものとなるからである。

コードの全容は以下の通り。

import tensorflow as tf
import numpy as np
import re



class Prepare_data:


    def __init__(self, file):
        self.chunk_size = 5
        self.vocabulary_size = 27
        self.file = open(file)


    def file_to_text(self):
        text = self.file.read()
        return text


    def text_to_array(self):
        text = self.file_to_text()
        text = text.lower()
        text = text.replace("\n", " ")
        text = re.sub(r"[^a-z ]", "", text)
        text = re.sub(r"[ ]+", " ", text)

        code_list = []
        for i in range(len(text)):
            if text[i] == " ":
                code_list.append(self.vocabulary_size - 1)
            else:
                code_list.append(ord(text[i])-ord("a"))
        code_array = np.array(code_list)
        return code_array


    def array_to_one_hot(self):
        array = self.text_to_array()
        one_hot = np.eye(self.vocabulary_size)[array]
        return one_hot


    def make_data(self):
        one_hot = self.array_to_one_hot()
        data_num = one_hot.shape[0] - self.chunk_size
        input_data = np.zeros([data_num, self.chunk_size, self.vocabulary_size])
        output_data = np.zeros([data_num, self.vocabulary_size])
        for i in range(data_num):
            output_data[i, :] = one_hot[i + self.chunk_size, :]
            for j in range(self.chunk_size):
                input_data[i, j, :] = one_hot[i + j, :]
        training_num = data_num * 4 // 5
        input_train = input_data[: training_num]
        output_train = output_data[: training_num]
        input_test = input_data[training_num :]
        output_test = output_data[training_num :]
        return input_train, output_train, input_test, output_test



class Rnn:


    def __init__(self):
        self.input_layer_size = 27
        self.hidden_layer_size = 30
        self.output_layer_size = 27
        self.chunk_size = 5
        self.batch_size = 128
        self.epoch = 100


    def inference(self, input_data, initial_state):
        w_hidden = tf.Variable(tf.random_normal([self.input_layer_size, self.hidden_layer_size], dtype="float64"))
        b_hidden = tf.Variable(tf.random_normal([self.hidden_layer_size], dtype="float64"))
        w_output = tf.Variable(tf.random_normal([self.hidden_layer_size, self.output_layer_size], dtype="float64"))
        b_output = tf.Variable(tf.random_normal([self.output_layer_size], dtype="float64"))

        input_data = tf.reshape(tf.transpose(input_data, [1, 0, 2]), [-1, self.input_layer_size])
        input_to_hidden = (tf.matmul(input_data, w_hidden) + b_hidden)
        input_to_hidden = tf.split(input_to_hidden, self.chunk_size)

        cell = tf.nn.rnn_cell.BasicRNNCell(self.hidden_layer_size, reuse=tf.AUTO_REUSE)
        hidden_to_output, final_state = tf.contrib.rnn.static_rnn(cell, input_to_hidden, initial_state=initial_state)

        output = (tf.matmul(hidden_to_output[-1], w_output) + b_output)

        return output


    def cost(self, output_data, labels):
        cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=labels, logits=output_data))
        return cost


    def training(self, cost):
        optimizer = tf.train.AdamOptimizer()
        training = optimizer.minimize(cost)
        return training


    def train(self, file):
        test_data = Prepare_data(file)
        input_train, output_train, input_test, output_test = test_data.make_data()
        zero_state = np.zeros([self.batch_size, self.hidden_layer_size], dtype="float64")

        input_data = tf.placeholder(tf.float64, [None, self.chunk_size, self.input_layer_size])
        labels = tf.placeholder(tf.float64, [None, self.output_layer_size])
        initial_state = tf.placeholder(tf.float64, [None, self.hidden_layer_size])

        prediction = self.inference(input_data, initial_state)
        cost = self.cost(prediction, labels)
        training = self.training(cost)
        correct = tf.equal(tf.argmax(prediction, 1), tf.argmax(labels, 1))
        accuracy = tf.reduce_mean(tf.cast(correct, dtype="float64"))

        with tf.Session() as sess:
            init = tf.global_variables_initializer()
            sess.run(init)

            for epoch in range(self.epoch):
                step = 1
                sum_cost = 0
                sum_acc = 0

                while self.batch_size * step < input_train.shape[0]:
                    input_batch = input_train[self.batch_size * (step - 1) : self.batch_size * step]
                    output_batch = output_train[self.batch_size * (step - 1) : self.batch_size * step]
                    c, _, a= sess.run([cost, training, accuracy], feed_dict = {input_data: input_batch, labels: output_batch, initial_state: zero_state})
                    sum_cost += c
                    sum_acc += a
                    step += 1

                ave_cost = sum_cost / step
                epoch_acc = sum_acc / step
                print("epoch: {0}, cost: {1}, epoch_accuracy: {2}".format(epoch, ave_cost, epoch_acc))

            print("Training finished")

            saver = tf.train.Saver()
            saver.save(sess, "./rnn_model")

            zero_state = np.zeros([input_test.shape[0], self.hidden_layer_size], dtype = "float64")

            a = sess.run(accuracy, feed_dict = {input_data: input_test, labels: output_test, initial_state: zero_state})
            print("accuracy: {0}".format(a))


    def predict(self, context):
        context = context.replace("\n", " ")
        context = re.sub(r"[^a-z ]", "", context)
        context = re.sub(r"[ ]+", " ", context)

        code_list = []
        for i in range(self.chunk_size):
            if context[- self.chunk_size + i] == " ":
                code_list.append(self.input_layer_size - 1)
            else:
                code_list.append(ord(context[- self.chunk_size + i])-ord("a"))
        code_array = np.array(code_list)
        one_hot = np.eye(self.input_layer_size)[code_array]
        input_pred = np.array([one_hot])

        zero_state = np.zeros([1, self.hidden_layer_size], dtype="float64")

        input_data = tf.placeholder(tf.float64, [None, self.chunk_size, self.input_layer_size])
        initial_state = tf.placeholder(tf.float64, [None, self.hidden_layer_size])

        prediction = tf.nn.softmax(self.inference(input_pred, initial_state))
        labels_pred = tf.argmax(prediction, 1)

        with tf.Session() as sess:
            saver = tf.train.Saver()
            saver.restore(sess, "./rnn_model")

            p, l = sess.run([prediction, labels_pred], feed_dict = {input_data: input_pred, initial_state: zero_state})

            for i in range(27):
                c = "_" if i == 26 else chr(i + ord("a"))
                print("{0}: {1}".format(c, p[0][i]))

            print("prediction: {0}{1}".format(context, "_" if l[0] == 26 else chr(l[0] + ord("a"))))




if __name__=="__main__":
    test_rnn = Rnn()
    test_rnn.train("make_data.txt")
    test_rnn.predict("convenenc")

正答率accuracyは4~4.5割程度であった。
また、最後のpredictメソッドを用いて、このコードでは単語convenienceの最後の文字を予測している。出力は以下の通り。

a: 0.019035454464732912
b: 4.738969637915792e-05
c: 0.002247770203502709
d: 0.001837168048159533
e: 0.49697183882149765
f: 6.776540899790401e-05
g: 6.803134720461048e-05
h: 0.005946570921486447
i: 0.1673174318507702
j: 7.938482418039402e-06
k: 0.05220353655820905
l: 0.047701385886800916
m: 0.0013250150854317872
n: 3.290372918878667e-06
o: 0.054324048979877804
p: 9.949375085362273e-05
q: 2.1549538581465823e-06
r: 0.0037619100217858355
s: 0.001999873710888758
t: 0.05791267551071772
u: 0.012747023559624206
v: 0.00020888998332423605
w: 0.0006592194567317431
x: 5.221744771573335e-09
y: 0.03779918142399007
z: 1.128392322579622e-07
_: 0.035704823438861145
prediction: convenience

1字ごとの確率を表示したのちに最終的な予測を表示している。今回は正しく予測できている。

参考

シンプルなRNNで文字レベルの言語モデルをTensorFlowで実装してみる - 今日も窓辺でプログラム
TensorFlow

建築構法概論

東大工学部建築学科の授業「建築工法概論」のメモ

土・煉瓦の特徴は?

これらが使われるのは主に以下の3つ。

  • 日干し煉瓦
  • 焼成煉瓦
  • 土壁

まず日干し煉瓦について。日干し煉瓦は現在でも乾燥地域で使われている。泥を型に入れて乾燥させ、組積造で組み上げられる。
次に焼成煉瓦について。これは土を成形して焼いたもの。そのため焼成煉瓦を使うためにはその地域に燃料があることが必要である。
これら煉瓦はもともと構造壁に用いられたが、現在ではRC造の非構造壁にも用いられる。人が運搬可能な大きさと重量のため、施工は容易ではあるが、手間はかかる。煉瓦の接着にはモルタルを用いる。積み方にはイギリス積みとフランス積みの2種類がある。
土壁は伝統的な壁仕上げとして用いられてきた。

石材の特徴は?

石材は直方体に切り出されて煉瓦同様に組積造で組み上げられる。この際そのまま積み上げる構法とモルタルで接着する構法に2種類がある。耐震性は低いため日本では塀や蔵にしか用いられず、大規模な石造建築は見られない。薄く加工することで、壁の仕上げ材としても用いられる。硬い石は外装に用いられる。これらの石は吸水率が低い。逆に柔らかい石は内装に用いられる。表面仕上げには様々なやり方があるが、代表的なものは本磨き、叩き仕上げの2種類。叩き仕上げは床材に行うと滑り止めとなる。

要求性能とは?

建物に要求される性能。以下のようなものがある。

  • 防水性能:外装の性能で、雨水が侵入しない性能。
  • 断熱性能:建物内外間での熱の移動を抑える性能。
  • 遮音性能:建物内外間で音を透過させない性能。
  • 耐震性能:地震時に部材が破損・脱落しない性能。
  • 対風圧性能:台風などの強風時に部材が破損・脱落しない性能。
  • 耐火性能:火災時に延焼・倒壊しない性能。
  • 日常安全性:建物使用者の安全性が確保されること。
  • 耐久性:各部材の安全性や基本性能が劣化しないこと。

木材の性質を挙げてみよう

メリットは以下。

  • 比強度が高い。
  • 比較的安価。

デメリットは以下。

  • 火災に弱い。
  • 材料ごとの性質のばらつきが大きい。
  • 異方性がある。

異方性について詳しく

木材は外から作用を受ける際、受ける方向によって応答が異なる。例えば、鉛直方向(L方向)は外力に強く、乾燥収縮が小さいが、半径方向(R方向)や、接線方向(T方向)は外力に弱く、乾燥収縮が大きい。

含水率とは?

木材の全重量に対する水分重量をパーセントで表したもの。木材は含水率15%程度まで乾燥させてから使う。

なぜ乾燥させる必要があるのか?

木材は乾燥収縮を起こしてひび割れを起こす。事前に十分乾燥させることでこれを防ぐことができる。

針葉樹と広葉樹では性能は変わるの?

主に針葉樹は構造体に使われ、広葉樹は家具や造作材(建築内の構造には直接関係ない仕上げ材)に使われることが多い。広葉樹は水分量で寸法が大きく変わり、重くて堅いためである。

屋根の種類まとめ

代表的な屋根の種類を以下にまとめた。

  • 切妻屋根:最もスタンダードな屋根。屋根の荷重を小屋梁で受ける。妻面に開口部を設けられるが、雨がかりになりやすい。
  • 寄棟屋根:妻面を無くし、四方を傾斜にしたことで雨をよく軒方向に流すことができる。
  • 入母屋屋根:寄棟屋根の上に切妻屋根を乗せた形をした屋根。伝統的な日本建築に見られる。
  • 方形屋根:寄棟屋根の棟木を一点に集中した形。
  • 片流し屋根:切妻屋根を半分にした形。小規模な木造住宅に見られる。

瓦の葺き方まとめ

一般的には垂木→野地板→防水材→葺き材の順番で施行される。葺き方の種類は以下のような感じ。

  • 本瓦葺き:平瓦と丸瓦を葺き土に固定させる。社寺建築に用いられる。
  • 桟瓦葺き:桟瓦とは波形の瓦のことで平瓦と丸瓦を一体とした形状のもの。これを野地板の上に敷設した瓦桟に引っ掛けて釘で留める。

板壁の張り方まとめ

  • 羽目板張り:板を同一平面に幅方向に張る。
  • 下見板張り:板の端をまた板で抑えて張る。

板張りには主に以上の2種類がある。下見板張りはさらに細かい分類がされ、以下のような種類がある。

  • 押し縁下見板張り:板の短辺を押し縁で押さえる。
  • ささらこ下見板張り:ささら型をした見切り縁で押さえる。
  • 南京下見板張り:板の下端でその下の板の上端を押さえる。
  • ドイツ下見板張り:板を相欠きで上下に繋げる。ただし、上端を大きく削ることで、その境目がよくわかる見た目となる。

天井の種類まとめ

  • 格天井:天井に格子を組み、できた正方形の箇所に鏡板をはめ込む。
  • 竿縁天井:吊り木で野縁を吊って、そこに竿縁を取り付ける。その上に天井板を乗せたもの。和室に用いられる。
  • 目透かし天井:天井板の継ぎ目に目地を設けたもの。
  • 打ち上げ天井:天井板を野縁に直接打ち付ける。

階段の種類まとめ

  • 側桁階段:段板の両端を桁に差し込んだもの。
  • ささら桁階段:段板を両側下から桁で支える。
  • 力桁階段:段板を真ん中下から1つの桁で支える。

ガラスの種類まとめ

  • フロート板ガラス:一般的なガラス。破損時には破片が飛び散るので危険。
  • 網入り板ガラス:防火用ガラス。
  • 強化ガラス:熱処理を施して強度を高くし、破損しても破片が細かくなるようにしたもの。
  • 倍強化ガラス:フロートガラスよりも対風圧強度、熱割れ強度が2倍程度のガラス。割れ方はフロートガラスに似ている。
  • 合わせガラス:複数のガラスを接着したガラス。耐貫通性が強く、破損時に破片が飛び散りにくい。
  • 複層ガラス:複数のガラスを間に空気層を挟んでくっつけたガラス。熱貫流率が小さいため、断熱性能が高い。

ツーバイフォー構法の種類

ツーバイフォーは以下の2種類に分けられる。

  • バルーンフレーム構法:土台、通し柱、屋根、各階の床の順に施行する。
  • プラットフォーム構法:土台、1階の壁、2階の床、2階の壁...の順に作る。

丸太組構法(校倉造・ログハウス)とは?

丸太を交互に井桁状に積み重ねて壁を形成する。現在では交差部に通しボルトを設ける。

参考

東京大学工学部建築学科「建築工法概論」授業スライド

HTS-2.3.2のソースコードをのぞいてそのDNNの構造を調べてみる

ざっとみてみた結果、DNNTraining.pyのmain関数の最初に定義される以下の変数configにDNNの構造が書かれているらしい。
100行目

config = DNNDataIO.load_config(args.config)

最初にこのconfigがどう指定されるかを追ってみる。
まず、DNNDataIO.pyのload_config関数は以下のようなもの。
245行目

def load_config(config_file, verbose=True):
    config_parser = ConfigParser.SafeConfigParser()
    config_parser.read(config_file)

    config = {}
    for section in config_parser.sections():
        for option in config_parser.options(section):
            config[option] = yaml.safe_load(config_parser.get(section, option))

    if verbose:
        print('Configuration Parameters[%d]' % len(config))
        print('              %-25s  s' % ('Parameter', 'Value'))
        for key in sorted(config.keys()):
            print('              %-25s  s' % (key, str(config[key])))
        print()

    config['num_io_units'] = (config['num_input_units'],
                              config['num_output_units'])

    return config

引数として指定されたconfigureファイルのsection中のoptionを辞書オブジェクトとして返す関数であることがわかった。
冒頭の話に戻ると、DNNTraining.pyでは引数としてargs.configを指定していたのであった。args.configとは何か。再びDNNTraining.pyをみてみる。
60行目

parser = argparse.ArgumentParser()

65行目

parser.add_argument('-C', metavar='cf', dest='config', type=str, required=True,
                    help='set config file to cf')

73行目

args = parser.parse_args()

DNNTraining.pyを実行する際指定するオプション-Cの値であることがわかった。
DNNTraining.pyはどのように実行され、オプション-Cには何が入るのであろうか。Training.plには以下の記述がある。
243行目

$cfg{'trj'}          = "$prjdir/configs/ver${ver}/trj_dnn.cnf";

269行目

 foreach $dir ( 'models', 'stats', 'edfiles', 'trees', 'gv', 'mspf', 'dnn', 'voices', 'gen', 'proto', 'configs' ) {
      mkdir "$prjdir/$dir",           0755;
      mkdir "$prjdir/$dir/ver${ver}", 0755;
   }

1974行目

open( CONF, ">$cfg{'trj'}" ) || die "Cannot open $!";
   print CONF "[Architecture]\n";
   print CONF "num_input_units: $nin\n";
   print CONF "num_hidden_units: [$nhid]\n";
   print CONF "num_output_units: $nout\n";
   print CONF "hidden_activation: \"$activations[$activation]\"\n";
   print CONF "output_activation: \"$activations[0]\"\n";
   print CONF "\n[Strategy]\n";
   print CONF "optimizer: \"$optimizers[$optimizer]\"\n";
   print CONF "learning_rate: $trjLearnRate\n";
   print CONF "gv_weight: $dnnGVWeight\n";
   print CONF "keep_prob: $keepProb\n";
   print CONF "queue_size: $queueSize\n";
   print CONF "batch_size: 1\n";
   print CONF "num_epochs: $nTrjEpoch\n";
   print CONF "num_threads: $nThread\n";
   print CONF "random_seed: $randomSeed\n";
   print CONF "frame_by_frame: 0\n";
   print CONF "adaptation: 0\n";
   print CONF "\n[Output]\n";
   print CONF "num_models_to_keep: $nKeep\n";
   print CONF "log_interval: $logInterval\n";
   print CONF "save_interval: $saveInterval\n";
   print CONF "\n[Others]\n";
   print CONF "all_spkrs: [\"$spkr\"]\n";
   print CONF "num_feature_dimensions: [";

930行目

if ($useDNN) {
      mkdir "$dnnmodelsdir{'trj'}", 0755;

      shell("cp $dnnmodels/model.ckpt.* $dnnmodelsdir{'trj'}");
      shell("$PYTHON $datdir/scripts/DNNTraining.py -C $cfg{'trj'} -S $scp{'tdn'} -H $dnnmodelsdir{'trj'} -z $datdir/stats -w $windir");
   }

プログラム実行時に新しくディレクトリを作り、そこにconfigureファイルを作って、それをオプションとして指定してPythonを動かしていた。
隠れユニットの数やoptimizerの種類などのDNNの構造が変数によって指定されているが、これら変数はConfig.pm.inで定義されているもので、その中で以下のように記述されている。

$useDNN       = @USEDNN@;         # train a deep neural network after HMM training
$nHiddenUnits = '@NHIDDENUNITS@'; # number of hidden units in each layer
$activation   = @ACTIVATION@;     # activation function for hidden units  (0 -> Linear, 1 -> Sigmoid, 2 -> Tanh, 3 -> ReLU)
$optimizer    = @OPTIMIZER@;      # optimizer (0 -> SGD, 1 -> Momentum, 2 -> AdaGrad, 3-> AdaDelta, 4 -> Adam, 5 -> RMSprop)
$learnRate    = @LEARNRATE@;      # learning rate
$trjLearnRate = @TRJLEARNRATE@;   # learning rate for trajectory training
$dnnGVWeight  = @DNNGVWEIGHT@;    # weight for GV
$keepProb     = @KEEPPROB@;       # probability for not randomly setting activities to zero
$queueSize    = @QUEUESIZE@;      # queue size
$batchSize    = @BATCHSIZE@;      # mini-batch size
$nEpoch       = @NEPOCH@;         # number of epochs
$nTrjEpoch    = @NTRJEPOCH@;      # number of epochs for trajectory training
$nThread      = @NTHREAD@;        # number of threads
$randomSeed   = @RANDOMSEED@;     # random seed used for initialization
$nKeep        = 5;                # number of models to keep
$logInterval  = 100;              # output training log at regular steps
$saveInterval = 10000;            # save model at regular steps

この@で囲まれた表記がよく分からなかった。何らかの方法でこの@で囲まれた変数に値を入力するらしい。例えば、活性化関数に関してはコメントによると

  • 0を入力すると線形関数(プログラム上は恒等関数であった)
  • 1を入力するとシグモイド関数
  • 2を入力するとハイパボリックタンジェント
  • 3を入力するとReLU関数

という具合に指定されるらしい。
結局configがどのように指定されるかは分からず仕舞いであった。
何らかの方法で指定されたconfigを参照してDNNTraining.pyにしたがってモデルが学習されるわけだが、詳しいことは別ファイルとしてDNNDefine.pyに記述されている。調べてわかったことは以下の通り。

  • 選択できる活性化関数は恒等関数、シグモイド関数、ハイパボリックタンジェント、ReLU関数の4種類。
  • 選択できるoptimizerはGradientDescentOptimizer、MomentumOptimizer、AdagradOptimizer、AdadeltaOptimizer、AdamOptimizer、RMSPropOptimizerの6種類。
  • 隠れ層ではmodeによって活性化関数に渡す式が変わる。modeが"SD"の時は簡単な式(Wx+b)だが、modeが"SAT"または"ADAPT"の時は重みWが2種類あり、auxially_inputという謎のinputが考慮されて複雑になる。modeを指定する条件は不明。
  • 出力層の活性化関数に渡す式は一様にWx+bであった。
  • コスト関数もcostとtrajectory_costの2種類用意されている。costの方は以下のように記述されている。
def cost(predicted_outputs, observed_outputs, variances):
    gconst = tf.cast(np.log(2.0 * np.pi), tf.float32)
    covdet = tf.reduce_mean(tf.log(variances))
    mahala = tf.reduce_mean(tf.divide(
        tf.square(observed_outputs - predicted_outputs), variances))
    cost = 0.5 * (gconst + covdet + mahala)
    return cost, predicted_outputs

式で表すと以下のようになる。

C\left(x\right) = \frac{1}{2}\left(\log\left(2\pi\right) + \frac{1}{n}\Sigma\left(\log\sigma + \frac{\left(y^\prime - y\right)^2}{\sigma}\right)\right)

n:出力テンソルの要素数,~~ y^\prime:DNN出力,~~ y:予想出力,~~ \sigma:分散共分散行列?)

trajectory_costの方はこれより複雑である。どちらのコスト関数が選択されるかはconfigで指定されるframe_by_frameによって決まるが、これが何を表すかは不明。

建築熱環境

東大工学部建築学科の授業「建築熱環境」のスライドを元に作成したノート。

建築の環境と空気状態量

ゼロエナジーバンドとは?

例えばエアコンの温度を20℃に設定し部屋の温度が20℃より少しでもずれるとエアコンが仕事をするとする。これではエネルギーを消費しすぎるのでエアコンが仕事をするまでに2~3℃猶予を持たせて20±2~3℃の範囲から外れた時に初めてエアコンが仕事をすると決めるとエネルギーの消費を抑えることができる。この±2~3℃の幅をゼロエナジーバンドという。

アクティブ建築・パッシブ建築とは?

アクティブ建築とは設備を導入して効率的に快適性を求めようとする建築のことで、パッシブ建築とはその逆になるべく自然のエネルギーを利用して快適性を求めようとする建築のこと。冷房を効かせた建物はアクティブだが、通風で部屋の温度を下げている建物はパッシブである。京都の聴竹居はパッシブ建築の良い例。

熱負荷とは?

室内を快適にするために取り出したり与える必要のある熱の量のこと。

湿り空気について

湿り空気=乾き空気+水蒸気
水蒸気の分子量が乾き空気に比べて小さいので、湿り空気の密度は乾き空気より小さくなる。
空気に含ませることができる水蒸気の量は温度によって上限があるため上限を超えると結露する。結露し始める温度を露点温度という。
結露には2種類あって、物体の表面で生じる表面結露、物体の内部で生じる内部結露である。これらは建材の腐敗やカビの発生に繋がる。
湿度の表し方には2種類あり、相対湿度絶対湿度である。相対湿度は空気中の水蒸気分圧の飽和空気と仮定した時の水蒸気分圧に対する比でありよく見る表示。一方で、絶対湿度とは乾き空気1kg中に含まれる水蒸気量(kg)のこと。

比エンタルピーとは?

そもそもエンタルピーHとは以下のようなエネルギー。

H = U + pV~~~(U: 内部エネルギー,~ p: 圧力, V: 体積)

比エンタルピーとは単位質量あたりのエンタルピー。エンタルピーはエネルギーであるから示量性をもっていて、
湿り空気のエンタルピー=乾き空気のエンタルピー+水蒸気のエンタルピー

湿球温度とは?

温度計の温度を感じる部分を湿らせたガーゼで覆うと水の気化熱で温度計は通常の温度計で測った値(乾球温度)より低い値を示す。ただし、空気中の水蒸気が飽和しているときは
湿球温度=乾球温度
湿球温度を測る器具としてアスマン通風乾湿計がある。これは乾球温度計湿球温度計を並べて通風したもの。

空気線図の読み方

空気線図から読み取れる値は以下のようなものなど。

  • 乾球温度
  • 湿球温度
  • 相対湿度
  • 絶対湿度
  • 比エンタルピー
  • 水蒸気分圧
  • 飽和度(比較湿度):空気中の水蒸気質量の飽和空気と仮定した時の水蒸気質量に対する比。相対湿度との違いが不明。
  • 比容積:単位質量あたりの体積。
  • 熱水分比:比エンタルピーの絶対湿度に対する比。
  • 顕熱比:顕熱量の全熱量に対する比。

外界気象

外界気象の7要素

これまでは建物内の環境について考えてきたが、外に目を向けて見る。外界気象の7要素とは以下のようなもの。

  • 気温
  • 相対湿度
  • 法線面直達日射
  • 水平面天空日射
  • 雲量
  • 風向
  • 風速

拡張アメダス気象データとは?

全国に842ヶ所ある気象庁の気象観測所、「アメダス」から作成された気象データ。記録する要素は以下の8要素。

  • 気温
  • 絶対湿度
  • 全天日射量
  • 大気放射量
  • 風向
  • 風速
  • 降水量
  • 日照時間

湿度は日・年を通じてどのように変化する?

日変化については気温と反対に挙動する。年変化については夏が高く、冬は太平洋側は低いが日本海側は高くなる。季節風の影響。

クリモグラフとは?

横軸に月平均相対湿度、縦軸に月平均気温をおいて1ヶ月ごとにプロットしたグラフ。

風配図とは?

ある地域のある季節・時刻に注目して、風の発生頻度を風向ごとにまとめたもの。特に頻度が高くなっている風を卓越風と呼ぶ。

陸風と海風はなぜ起こる?

比熱の関係で海に比べて陸は暖まりやすく冷めやすい。例えば昼は海よりも陸が暖かいので陸では上昇気流が発生し、地上付近の気圧は海に比べて低くなるので海から陸へ風が吹く。同様に夜は陸から海へ風が吹く。

太陽高度・太陽方位角とは?

太陽高度とは地球上のある地点で地面を平面と考えたとき、その地点と太陽を結ぶ直線が平面となす角のこと。太陽方位角とはその地点と太陽を結ぶ直線を平面に正投影した直線が地点から平面を通って南に伸ばした直線となす角のこと。

赤緯とは?

太陽軌道と赤道のズレ角度。夏至に最大(23.4º)となって冬至に最小(-23.4º)となる。春分秋分の日は0º。

時角とは?

時間を角度で表したもの。正午が0ºで-180~180º。このとき用いられる時間は太陽を基準にした真太陽時に平均太陽時も考慮したもの。

太陽から来る放射エネルギーについて

太陽から地球に来る放射エネルギーのうち、約55%は地表に達する前に大気によって散乱・吸収されるが、散乱も吸収もされずに地表に達したものを直達日射と呼ぶ。大気によって散乱されたものが地表に達したものを天空日射と呼ぶ。これら2つを合わせたものを全天日射と呼ぶ。日射を吸収した大気はエネルギーを放射する(大気放射)。地表からも同様にエネルギーが放射され(地表面放射)、これら差分を夜間放射と呼ぶ。夜間のみに起こるものではないが、夜間は全天日射がなくなるため、地表に降り注ぐ放射エネルギーは夜間放射のみとなる。まとめると以下のようになる。
全天日射=直達日射+天空日射
夜間放射=大気放射ー地表面放射

物体の放射についてもっと詳しく

物体は全て表面から電磁波を発しており、その強さは表面の絶対温度の4乗に比例する。表面が黒のとき(入射するエネルギーを全て吸収するとき)電磁波は最も強くなる。

太陽エネルギーに関する数について

大気圏外において太陽の放射エネルギーを受けることを考える。光線に垂直な面(法線面)で受けた際の単位面積あたりの放射エネルギーを太陽定数(W/㎡)という。同じものを大気を通過した放射エネルギーについて考えたのが法線面直達日射(W/㎡)と呼ぶ。これには天空日射は含まれない。天空日射を水平面で受けたときの単位面積あたりの放射エネルギーを水平面天空日射(W/㎡)と呼ぶ。大気の澄み具合の指標として大気透過率という量があり、以下のような量である。

大気透過率=\frac{法線面直達日射}{太陽定数}

水平面に降り注ぐ全天日射J_Hは以下の式で求まる。

J_H = J_D\sin h + J_S~~~~~(J_D: 法線面直達日射,~ J_S: 水平面天空日射,~ h: 太陽高度)

このJ_Hは広く観測されているもので、このデータを用いてJ_DJ_Sの値が決められる。これを直散分離と呼ぶ。直散分離で分かったJ_DJ_Sの値は任意の傾斜角の建築外壁のエネルギー入射を検討に用いられる。

日射の調節について

日射量を調節するのにはあらゆる方法がある。

  • 東西に長い建築は日射を大量に取り入れる。
  • 庇のある窓は夏は日射をカットし、冬はよく日射を取り入れる。
  • 落葉樹は夏は日射をカットし、冬はよく日射を取り入れる。
  • 季節によってモードを変えられるダブルスキンサッシは夏は窓間の空気を換気することで遮熱し、冬は窓間の空気を閉じ込めることで断熱する。

室内熱・空気環境

平均放射温度(MRT)とは?

室内にいる人や物体が、不均一な温度分布をもつ壁から受ける放射熱を考える。この放射熱と同じ量の放射熱を発する均一な温度分布をもつ壁を仮定したとき、その理想的な壁の温度を平均放射温度(MRT)という。

グローブ温度とは?

室内の物体は壁と熱の放射や空気の対流によってエネルギーをやりとりしている。そのやりとりについて平衡状態となったときの温度をグローブ温度という。温度計の温度を感じる部分を直径15cmのつや消し黒色塗料を施した中空銅球の中に入れたグローブ温度計はこのグローブ温度を測るための器具である。

シックビルディング・シックハウス症候群とは?

建築の省エネ化のために建物を気密化した結果、新鮮な外気が取り入れられなくなったり、材料に揮発性有機化合物(VOC)を用いたことが原因となってその建物内にいる人が体の不調を起こすこと。

総揮発性有機化合物(TVOC)とは?

VOCの量の指標となる値。つまり室内環境汚染の指標。

建築物衛生法とは?

名前の通り、建築物における最低限の衛生基準を定めた法律。

温冷感指標

温冷感指標の要素について

暖かさ・涼しさをどの程度感じるかの目安である温冷感指標は環境側の要素として以下の4要素をもつ。

  • 温度
  • 相対湿度(あるいは絶対湿度)
  • 放射温度
  • 気流速度

また、人体側の要素として以下の2要素をもつ。

作用温度とは?

室内の人は放射によって壁と熱のやりとりをし、対流によって空気と熱のやりとりをしている。このやりとりが同じ量行われる温度が均一な理想的な空間を考えた際、その理想的な空間の温度が作用温度である。

等価温度とは?

作用温度に気流による冷却効果を加味したもの。さらに着衣量も加味した等価温度も提案されている。

有効温度(ET)とは?

現在の気温、湿度、風速に対して、湿度100%、風速0m/sの理想的な状態を考える。現在の状態と同じ温冷感になるように理想的な状態の気温を定めたとき、その気温を有効気温(ET)という。これにさらに放射の影響も加味したものを修正有効温度と呼ぶ。

新有効温度(ET*)とは?

湿度100%を基準とする有効温度に対して湿度50%を基準とし、人体側の2要素も考慮したものを新有効温度(ET*)と呼ぶ。さらに、平均放射温度が気温となるような状態を基準としたものを標準新有効温度(SET*)と呼ぶ。

予測温冷感申告(PMV)とは?

温冷感指標6要素を元にした温冷感を表す量。

予想不満足者率(PPD)とは?

その名の通り、室内環境の不快度を表す指標で、PMDの関数である。

不快指数(DI)とは?

予想不満足者率と同様に室内環境の不快度を表す指標であるが、蒸暑による不快度に限定されている。

湿球グローブ温度(WBGT)とは?

熱帯地域での軍事訓練の限界条件を検討する目的で提案されたもの。グローブ温度、気温、湿球温度で表される。

ドラフトとは?

空調をする際の必要以上に強い気流は不快感の原因であるが、その望まれない局部気流をドラフトと呼ぶ。

換気と通風

換気と通風の違いは?

換気は室内空気を綺麗に保つために行うが、通風は涼感を得るために行うもの。

換気について

機械換気と自然換気の二種類があり、そのうち機械換気には以下の3種類がある。

  • 第1種:給気排気共に機械換気で行う。
  • 第2種:給気のみ機械換気で行うもので、室内は正圧となる。
  • 第3種:排気のみ機械換気で行うもので、室内は負圧となる。

また、自然換気は開口部前後の空気の圧力差を利用するもので、以下の2種類がある。

  • 風力換気:風圧を利用する。壁面上の位置によって風圧が変わることを考慮した係数は風圧係数と呼ばれる。

p_w = C \cdot \frac{1}{2} \rho V^2~~~~~(p_w: 風圧力, C: 風圧係数,~ \rho: 空気密度, V:風速)

  • 温度差換気:温度差で生ずる空気の浮力を利用する。

p_i = p_{i0} + g\left(\rho_o - \rho_i\right)h

(p_i: 圧力差,~ p_{i0}: 室内での基準高さの圧力,~ h: 測定する基準からの高さ)

中性帯とは?

室内と外気で圧力が等しくなる位置。

開口部を通過する風量について

通過風量Qは断面積と風速の積で表される。

Q = vA = \alpha A \sqrt{\frac{2\left(p_1 - p_2\right)}{\rho}}~~~~~(\alpha: 流量係数)

この流量係数と断面積の積\alpha A実行面積(相当開口面積)と呼ばれ、換気用に開口部を2つ並列、直列に並べた際の値は以下のようにして計算する。

並列合成:\left(\alpha A\right)_{12} = \alpha_1 A_1 + \alpha_2 A_2

直列合成:\left(\alpha A\right)_{12} = \frac{1}{\sqrt{\left(\frac{1}{\alpha_1 A_1}\right)^2 + \left(\frac{1}{\alpha_2 A_2}\right)^2}}

よって風力換気の通過風量は以下のようになる。

Q = \left(\alpha A\right)_{12}V\sqrt{C_1 - C_2}

また、温度差換気の通過風量は以下のようになる。

Q = \left(\alpha A\right)_{12}\sqrt{2g\left(1 - \frac{\rho_i}{\rho_o}\right)h} = \left(\alpha A\right)_{12}\sqrt{2g\left(1 - \frac{T_0}{T_i}\right)h}

風力換気は比較的大きな換気量が得られるが、風速に左右される。温度差換気は換気量が比較的小さい代わりに安定した換気ができる。

自然換気の排気経路について

自然換気の排気経路には以下の3種類などがある。

  • 通風型:風上、風下にそれぞれ開口部を設ける。
  • ボイド型:建物屋上に流出口を設ける。
  • シャフト型:階段室や専用の縦シャフトに作用する温度差を利用する。

必要換気量

室内に汚染物質が増えないように必要な換気量。

必要換気量=\frac{室内における汚染物質発生量}{\left(外気における汚染物質濃度 - 室内における汚染物質濃度\right)}

貫流

貫流とは?

壁を挟んだ熱の流れ。壁内部で起こる熱伝導と壁で起こる両表面の熱伝達で構成される。

貫流率とは?

単位面積当たりの壁を挟んで1℃の温度差があるときに流れる熱。壁の熱抵抗の逆数で表される。壁の熱抵抗R

R = \frac{1}{\alpha_o} + \Sigma^n_{i=1}R_i + \frac{1}{\alpha_i}

\left(\alpha_o: 屋外側熱伝達率, \alpha_i: 室内側熱伝達率,  R_i: 各材料の熱抵抗\right)

R_i = \frac{d_i}{\lambda_i}~~~~~(d_i: 各材料の厚さ, \lambda_i: 各材料の熱伝導率)

参考

東京大学工学部建築学科「建築熱環境」授業スライド(赤司泰義)

DNN音声合成について

引用は全て「深層学習に基づく統計的音声合成」(https://www.jstage.jst.go.jp/article/jasj/73/1/73_55/_pdf)から。

統計的音声合成とは

音声合成の問題をテキスト解析部と波形生成部に分解すると,テキストから音素,品詞,単語などを表現する離散値系列(言語特徴量系列)を推定する問題と,言語特徴量系列から音声の特徴を表す連続値系列(音響特徴量系列)を推定し,音響特徴量系列から波形を合成する問題へと分けることができる。

文字列から音声に変換する過程をテキスト解析とそれを元にした波形生成の2つに分けて考えている。

統計的音声合成は,言語情報量系列 l から音響特徴量系列 o が生成される確率P(o | l, λ) を音響モデルと呼ばれる統計モデルによってモデル化することで実現される。

波形生成をする際に、テキスト解析から得られる音素や品詞、単語といった特徴(言語特徴量)から波形のスペクトルのパラメータ、基本周波数といった特徴(音響特徴量)を推定するが、統計的音声合成とはその推定に学習データから得られる確率をモデル化したものを使うような音声合成法である。
同論文(https://www.jstage.jst.go.jp/article/jasj/73/1/73_55/_pdf)から引用した以下の図は統計的音声合成の全体像を理解するのに役に立った。

f:id:k17trpsynth:20180131183901p:plain

DNN音声合成における学習の特徴

DNN に基づく音声合成(DNN 音声合成)では,言語特徴量から音響特徴量を予測する回帰問題にDNN を利用する。

DNN 音声合成では,あらかじめフレーム単位の音響特徴量系列と音素単位の言語特徴量系列の対応(音素境界)を設定し,フレーム単位の音響特徴量と言語特徴量の対を用意する。

コンテクストに関する質問に対する二値(0/1),又は連続値での回答を連結し,数値データとして表現された言語特徴量を用意する。

決定木でしか分類できなかったHMMとは違い、DNNでは連続値で分類することができる。例えば、アクセント型を分類する際、HMMでは「0型か?→はい/いいえ」「1型か?→はい/いいえ」...と分類していたのに対してDNNでは「アクセント型は?→x型」と答えることができる。

DNN音声合成における合成の特徴

まず,継続長モデルを用いて音素継続長を推定し,テキストと音素継続長からフレーム単位の言語特徴量を抽出する。

文字列からは時間は読み取れないので別に用意したモデルを用いて文字列を話す長さを推定し、フレームごとに分割してそれぞれのフレームに言語特徴量のラベルづけをする。

次に,抽出された言語特徴量を DNN へと入力し,DNN からの出力を連結することで音響特徴量系列を得る。DNNはフレームごとに独立に音響特徴量の推定を行うため,得られる音響特徴量系列は合成音声の品質を低下させるような不連続がしばしば含まれる。このため,DNN 音声合成においても HMM 音声合成と同様に,動的特徴量を用いたパラメータ生成アルゴリズムを利用することで合成音声の品質が改善することが知られている。

DNNはフレーム単位で音響特徴量を推定するためその境目で基本周波数などが不連続だったりする場合がある。これでは合成音声は不自然なものになってしまうので、フレームごとのDNNの出力をまとめて1つの出力として考え、それらまとめた出力が、どの「動的特徴量までも含めた音響特徴量」に対応するかという確率を考えて、その確率が最大となるような静的特徴量から波形を作る。音響特徴量を選ぶ際に動的特徴量も考慮することにより、フレーム間の不連続さが解消され、合成音声の不自然さを減らすことができる。