Tensorflow 对数几率回归

Submitted by Lizhe on Tue, 08/22/2017 - 13:47

线性回归模型所预测的是一个连续值或任意实数

对数几率回归则用于预测 Y/N 

f(x) = 1/(1+e-x)

log

随着x值(横轴)的变化, y值(纵轴)会在0~1 这个区间内变化, 这里1表示Y, 0表示位N 

这个函数接收单个输入值所以这里如果我们需要使用多个输入值的话,需要将多个值合并成单个值使用

例如如果数据中是 

姓名    年龄    性别

Tom    20       Male

可能需要合并成 Tom_20_Male之后才能放入上面的公式参与计算

这里的例子是计算泰坦尼克的乘客的幸存概率,原始数据为

PassengerId,Survived,Pclass,Name,Sex,Age,SibSp,Parch,Ticket,Fare,Cabin,Embarked
1,0,3,"Braund, Mr. Owen Harris",male,22,1,0,A/5 21171,7.25,,S
2,1,1,"Cumings, Mrs. John Bradley (Florence Briggs Thayer)",female,38,1,0,PC 17599,71.2833,C85,C
3,1,3,"Heikkinen, Miss. Laina",female,26,0,0,STON/O2. 3101282,7.925,,S

下载地址 https://www.kaggle.com/c/titanic/data (需要注册)

你也可以直接从本网站获取 http://lizhe.name/sites/default/files/download/train.csv

其中我们只抽取三种数据作为计算依据

仓位等级 性别 和 年龄

这些数据均为 "属性数据" 即没有关联性,只是表示 是 或者 否 的数据

所以最好的办法是将他们作为一个N维的布尔值来使用然后得到 5 个布尔值

is_first_class  是否为一等舱

is_second_class 是否为二等舱

is_third_class 是否为三等舱

gender 性别 (0为男性,1为女性)

age 年龄

 

对于上面说到的第一步我们需要将5个属性值合并成一个值

W = tf.Variable(tf.zeros([5, 1]), name="weights")#变量权值

b = tf.Variable(0., name="bias")#线性函数常量,模型偏置

def combine_inputs(X):#输入值合并

    print "function: combine_inputs"

    return tf.matmul(X, W) + b

这里因为每行有5个数据(列), 所以需要一个使用一个[5,1]的向量作为乘数 (5行1列)

然后加上偏移量b

我们得到一个 N行5列的矩阵 和 一个 5行1列的矩阵的乘积 与 偏移量b 的和

 

然后将合并函数导入推断函数

def inference(X):#计算返回推断模型输出(数据X)

    print "function: inference"

    return tf.sigmoid(combine_inputs(X))#调用概率分布函数

 

计算损失的函数为 交叉熵损失函数

ef loss(X, Y):#计算损失(训练数据X及期望输出Y)

    print "function: loss"

    #求平均值
    return tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(combine_inputs(X),Y))

 

接着定义读取csv文件的函数

def read_csv(batch_size, file_name, record_defaults):#从csv文件读取数据,加载解析,创建批次读取张量多行数据

    filename_queue = tf.train.string_input_producer([os.path.join(os.getcwd(), file_name)])

    reader = tf.TextLineReader(skip_header_lines=1)

    key, value = reader.read(filename_queue)

    decoded = tf.decode_csv(value, record_defaults=record_defaults)#字符串(文本行)转换到指定默认值张量列元组,为每列设置数据类型

    return tf.train.shuffle_batch(decoded, batch_size=batch_size, capacity=batch_size * 50, min_after_dequeue=batch_size)#读取文件,加载张量batch_size行

 

key, value = reader.read(filename_queue) 会读取csv文件中的一行, 内容保存到value变量中

此时 print(key,"::",value) 会得到 

(<tf.Tensor 'ReaderRead:0' shape=() dtype=string>, '::', <tf.Tensor 'ReaderRead:1' shape=() dtype=string>)

也就是一行数据

这里你可能会奇怪这个read方法命名没有在loop循环中, 是如何被调用了多次的

tensorflow的执行和其他框架有一些不一样, 它的代码并不是实时调用的逻辑代码, 而是为了生成数据流图而设计的

这里只是定义了一个图(流程)

train_op = train(total_loss)  

                                                  ==> total_loss = loss(X, Y)

                                                                                                   ==> X, Y = inputs()

                                                                                                                                      ==> read_csv(...)

真正的运算行为要等到sess.run([train_op]) 执行才会完整的开始调用这些逻辑, 而sess.run([train_op])这条语句恰恰就在loop循环中

 

 

decoded = tf.decode_csv(value, record_defaults=record_defaults) 把上一条语句得到的内容value转换成tensor对象的列
print(decoded) 输出得到一共12个tensor对象,分别对应不同的数据类型

[<tf.Tensor 'DecodeCSV:0' shape=() dtype=float32>,

<tf.Tensor 'DecodeCSV:1' shape=() dtype=float32>,

<tf.Tensor 'DecodeCSV:2' shape=() dtype=int32>,

<tf.Tensor 'DecodeCSV:3' shape=() dtype=string>,

<tf.Tensor 'DecodeCSV:4' shape=() dtype=string>,

<tf.Tensor 'DecodeCSV:5' shape=() dtype=float32>,

<tf.Tensor 'DecodeCSV:6' shape=() dtype=float32>,

<tf.Tensor 'DecodeCSV:7' shape=() dtype=float32>,

<tf.Tensor 'DecodeCSV:8' shape=() dtype=string>,

<tf.Tensor 'DecodeCSV:9' shape=() dtype=float32>,

<tf.Tensor 'DecodeCSV:10' shape=() dtype=string>,

<tf.Tensor 'DecodeCSV:11' shape=() dtype=string>]

 

return tf.train.shuffle_batch(decoded, batch_size=batch_size, capacity=batch_size * 50, min_after_dequeue=batch_size)

Creates batches by randomly shuffling tensors.

也就是对原数据进行"洗牌", 有一个需要特别注意的地方reader.read函数本身返回一行数据

但是这里因为后面调用的时候传入的batch_size参数为100,所以在洗牌动作运行结束之后,我们已经读取了100行数据

(当然这些代码也要等到真正run的时候才开始执行)

 

inputs() 函数会从read_csv函数的返回值中获得数据并赋值给变量

需要注意的是这里得到了12个tensor对象, 每个tensor对象都包含了100个数据 (可以理解成向量)

tensor对象_1  (passenger_id) id_1, id_2, id_3 ... id_100
tensor对象_2  (survived) survived_1, survived_2 ... survived_100
tensor对象_3  (passenger_id) pclass_1, pclass_2 ... pclass_100

 

passenger_id, survived, pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked = \
        read_csv(100, "train.csv", [[0.0], [0.0], [0], [""], [""], [0.0], [0.0], [0.0], [""], [0.0], [""], [""]])

然后将除了年龄之外的数据(字符串) 转换成数字, 年龄本身就是数字所以不用转换了

这些转换并不是转行单一常量,而是在转换100行1列的矩阵

    is_first_class = tf.to_float(tf.equal(pclass, [1]))#一等票

    is_second_class = tf.to_float(tf.equal(pclass, [2]))#二等票

    is_third_class = tf.to_float(tf.equal(pclass, [3]))#三等票

    gender = tf.to_float(tf.equal(sex, ["female"]))#性别,男性为0,女性为1

 

然后是tf.pack函数

tf.pack([is_first_class,is_second_class,is_third_class,gender,age])

tf.pack(values, axis=0, name=”pack”) 
Packs a list of rank-R tensors into one rank-(R+1) tensor 
将一个R维张量列表沿着axis轴组合成一个R+1维的张量。

  # 'x' is [1, 4]
  # 'y' is [2, 5]
  # 'z' is [3, 6]
  pack([x, y, z]) => [[1, 4], [2, 5], [3, 6]]  # Pack along first dim.
  pack([x, y, z], axis=1) => [[1, 2, 3], [4, 5, 6]]

pack函数的输出为

Tensor("pack:0", shape=(5, 100), dtype=float32)

这个函数将输入的 5个tensor对象组 合并成为 一个5行的矩阵, 因为每行有一个tensor对象,而每个tensor对象包含100个值,所以看起来是这样的

[

    [tensor_one_1 .... 100],

    [tensor_two_1 .... 100],

    [tensor_three_1 .... 100],

    ...

]

然后对5行100列的矩阵进行转置得到一个100行5列的矩阵

x =  [[1 2 3]
        [4 5 6]]


tf.transpose(x) ==> [[1 4]
                                [2 5]
                                [3 6]]

 

因为属性为100行5列的矩阵

所以结论也需要转换成对应的格式 (100行1列)

survived = tf.reshape(survived, [100, 1])

 

# -*- coding: utf-8 -*-
import tensorflow as tf

import os

#参数变量初始化

W = tf.Variable(tf.zeros([5, 1]), name="weights")#变量权值

b = tf.Variable(0., name="bias")#线性函数常量,模型偏置

def combine_inputs(X):#输入值合并

    print "function: combine_inputs"

    return tf.matmul(X, W) + b

def inference(X):#计算返回推断模型输出(数据X)

    print "function: inference"

    return tf.sigmoid(combine_inputs(X))#调用概率分布函数

def loss(X, Y):#计算损失(训练数据X及期望输出Y)

    print "function: loss"

    #求平均值
    return tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(combine_inputs(X),Y))

def read_csv(batch_size, file_name, record_defaults):#从csv文件读取数据,加载解析,创建批次读取张量多行数据

    filename_queue = tf.train.string_input_producer([os.path.join(os.getcwd(), file_name)])

    reader = tf.TextLineReader(skip_header_lines=1)

    key, value = reader.read(filename_queue)

    decoded = tf.decode_csv(value, record_defaults=record_defaults)#字符串(文本行)转换到指定默认值张量列元组,为每列设置数据类型

    return tf.train.shuffle_batch(decoded, batch_size=batch_size, capacity=batch_size * 50, min_after_dequeue=batch_size)#读取文件,加载张量batch_size行

def inputs():#读取或生成训练数据X及期望输出Y

    print "function: inputs"

    #数据来源:https://www.kaggle.com/c/titanic/data

    #模型依据乘客年龄、性别、船票等级推断是否能够幸存

    passenger_id, survived, pclass, name, sex, age, sibsp, parch, ticket, fare, cabin, embarked = \
        read_csv(100, "train.csv", [[0.0], [0.0], [0], [""], [""], [0.0], [0.0], [0.0], [""], [0.0], [""], [""]])

    #转换属性数据

    is_first_class = tf.to_float(tf.equal(pclass, [1]))#一等票

    is_second_class = tf.to_float(tf.equal(pclass, [2]))#二等票

    is_third_class = tf.to_float(tf.equal(pclass, [3]))#三等票

    gender = tf.to_float(tf.equal(sex, ["female"]))#性别,男性为0,女性为1

    #所有特征排列矩阵,矩阵转置,每行一样本,每列一特征
    features = tf.transpose(tf.pack([is_first_class,is_second_class,is_third_class,gender,age]))

    survived = tf.reshape(survived, [100, 1])

    return features, survived

def train(total_loss):#训练或调整模型参数(计算总损失)

    print "function: train"

    learning_rate = 0.01

    return tf.train.GradientDescentOptimizer(learning_rate).minimize(total_loss)

def evaluate(sess, X, Y):#评估训练模型

    print "function: evaluate"

    predicted = tf.cast(inference(X) > 0.5, tf.float32)#样本输出大于0.5转换为正回答

    print sess.run(tf.reduce_mean(tf.cast(tf.equal(predicted, Y), tf.float32)))#统计所有正确预测样本数,除以批次样本总数,得到正确预测百分比

#会话对象启动数据流图,搭建流程

sess = tf.Session()

print "Session: start"

init = tf.initialize_all_variables()
sess.run(init)

X, Y = inputs()

total_loss = loss(X, Y)

train_op = train(total_loss)

coord = tf.train.Coordinator()

threads = tf.train.start_queue_runners(sess=sess, coord=coord)

training_steps = 100#实际训练迭代次数

for step in range(training_steps):#实际训练闭环

    sess.run([train_op])

    if step % 10 == 0:#查看训练过程损失递减

        print str(step)+ " loss: ", sess.run([total_loss])

print str(training_steps) + " final loss: ", sess.run([total_loss])

evaluate(sess, X, Y)#模型评估

import time

time.sleep(5)

coord.join(threads)

sess.close()