tensorflow 数据归一化_【TensorFlow实现机器学习方法】KNN(K近邻算法)实现预测房屋价格...

93cdbc1d1f50cb88acf30eb12cb95299.png

一、前言

机器学习KNN算法(K近邻算法)的总体理论很简单不在这里赘述了。

二、数据集准备

这里使用比较古老的数据集,是房屋预测的数据集 下载地址 https://archive.ics.uci.edu/ml/machine-learning-databases/housing/housing.data

11b05b63ab44959afb50067a48bf8308.png

有14列,分别是13个特征,1个房屋价格值

34c92ea69f7ee94680563e9bf5385aab.png
  1. CRIM 村庄的人均犯罪率
  2. ZN 占地面积超过25,000平方尺的住宅用地比例。
  3. INDUS 每个城镇非零售业务的比例
  4. CHAS Charles River虚拟变量(如果管道限制河流则= 1;否则为0)
  5. NOX 一氧化氮浓度(每千万份)
  6. RM 每个居所的平均的屋子数量
  7. AGE 1940年以前建造的自住单位比例
  8. DIS 到波士顿五个就业中心的加权距离
  9. RAD 径向高速公路的可达性指数
  10. TAX 每10,000美元的全额房产税率
  11. PTRATIO 城镇的师生比例
  12. B 城市的黑人比例
  13. LSTAT 人口减少的百分比
  14. MEDV 自住房屋的中位数价值

三、数据集预处理

一般对于txt类似的数据集读入都是差不多的步骤

path = 'XXX.txt'
fr = open(path) #打开文件
lines = fr.readlines() #读入所有行
dataset = []
for line in lines: #遍历每一行(这里的每一行都是string)
    # 将整个string左右两边的空格和换行符去掉
    # 可以理解为只保留最外面的两层
    line = line.strip() 
    # 用空格将string分割成list
    line = line.split('t')
    # 将list每个元素的string转换为float
    line = [float(i) for i in line]
    dataset.append(line)

但是这里为.data,其实和txt文件处理相似,由于数据集的每列之间的空格个数不相同,有的是一个空格,有个两个,有的是三个,这里string的split需要用正则化re.split来代替具体代码如下:

path = 'housing.data'
fr = open(path)
lines = fr.readlines()
dataset = []
for line in lines:
    line = line.strip()
    line = re.split(r' +', line) # re处理
    line = [float(i) for i in line]
    dataset.append(line)

这里的feature我们不全都用,只用一部分,因为一些feature与最后的预测没有很密切的关系 这里我们采用pandas来进行操作作为容易,还有一点很关键作为特征,有的数大小差距很大,如下图所示:

cce176fbb1f8b9da997901498f5f846a.png

所标三列就与其他差别很大,这里只是举例,所以需要做归一化处理,利用(data-min)/(max-min)

housing_header = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']
# 需要用到的column
cols_used = ['CRIM', 'INDUS', 'NOX', 'RM', 'AGE', 'DIS', 'TAX', 'PTRATIO', 'B', 'LSTAT'] 
dataset_array = np.array(dataset)
# 转换为pandas
dataset_pd = pd.DataFrame(dataset_array, columns=housing_header)
# 取出特征数据
housing_data = dataset_pd.get(cols_used)
# 取出房价数据
labels = dataset_pd.get('MEDV')
housing_data = np.array(housing_data)
# 对特征数据进行归一化处理 
# ptp(0)为每列求出每列数据中的range = max-min
housing_data = (housing_data - housing_data.min(0))/housing_data.ptp(0)
# [:, np.newaxis]为行向量转换为列向量
labels = np.array(labels)[:, np.newaxis]
print(housing_data)
print(labels)

最终得到的数据为 housing_data:

339eaa578c5e1dcf81ad874ac288872a.png

labels:

d0b091c86b461c72b46eec3139b3215a.png

## 四、分为训练集与测试集 一般来说 训练集和测试集需要随机分配,如果没有验证集的部分,一般为8:2 其实这里可以用sklearn来进行分配操作,但这里我选择用numpy来操作,更为基础写法

np.random.seed(22)
# 生成随机的index
train_ratio = 0.8
data_size = len(housing_data)
train_index = np.random.choice(a=data_size, size=round(data_size*train_ratio), replace=False)
test_index = np.array(list(set(range(data_size))-set(train_index)))
# 生成训练集和测试集
x_train = housing_data[train_index]
y_train = labels[train_index]
x_test = housing_data[test_index]
y_test = labels[test_index]

## 五、Tensorflow实现KNN ### 1. 计算图的输入 这里不必过多解释, tensorflow的输入 x的每一个输入为 上面处理之后保留num个features的数据 y的每一个输入为 加格,即一个数据

x_train_placeholder = tf.placeholder(tf.float32, [None, num_features])
x_test_placeholder = tf.placeholder(tf.float32, [None, num_features])

y_train_placeholder = tf.placeholder(tf.float32, [None, 1])
y_test_placeholder = tf.placeholder(tf.float32, [None, 1])

2. KNN

2.1 distance实现

故名思议: KNN-K近邻算法既 取前k个distance进行判断,如果是classifications问题其实很好判断,只需要选一种计算距离的方式即可,如L2(欧式距离)

dadd1d9c410c8633a64729e4f203a3ad.png

也可以是L1

6ea64b9e74c6d1cd3278885eb9b02dda.png

因为此处需要用到tensorflow来编写,需要注意的是矩阵运算,正常来说应该是一个testdata与全部的traindata进行计算,但这里采用bathsize大小的testdata与traindata进行继续,这里集中于矩阵和维度的变换上,先上code再解释 distance计算

distance = tf.sqrt(tf.reduce_sum(tf.square(tf.subtract(x_train_placeholder, tf.expand_dims(x_test_placeholder, 1))), reduction_indices=[1]))

这句话其实也可以分解为

x_test_placeholder_expansion = tf.expand_dims(x_test_placeholder, 1) #增加一个维度
x_substract = x_train_placeholder - x_test_placeholder_expansion #相减
x_square = tf.square(x_substract) #平方
x_square_sum = tf.reduce_sum(x_square, reduction_indices=[1]) # 每个相加
x_sqrt = tf.sqrt(x_square_sum) # 开方

这几句话主要是做了一个distance计算的过程, 就是我们最为熟悉的欧式距离 大概都能看懂,这里主要是讲解两句话

x_test_placeholder_expansion = tf.expand_dims(x_test_placeholder, 1)

这句话主要是因为 这里我们所要做的是每个testdata都与所有的traindata做计算,所以这里需要把testdata扩展一个维度 这样一来就相当于x_train_placeholder - x_test_placeholder_expansion时直接相当于batchsize大小的testdata每一个自己为一个维度的数据都与所有的traindata进行相减。

x_square_sum = tf.reduce_sum(x_square, reduction_indices=[1]) # 每个相加

这句话做的是将你之前计算出来的每个testdata与所有的traindata计算出来的每个feature之间的相减平方进行相加。

如果以上两句大家实在是无法明白,请自行 玩转以下tf的这两个函数即可明白用意。

以上计算距离的方式再预测问题中效果并不好,而是用这里介绍的另一种计算方式L1,代码如下:

distance = tf.reduce_sum(tf.abs(x_train_placeholder - tf.expand_dims(x_test_placeholder , 1)), axis=2)

2.2 排序实现

因为这里不是分类问题,而是做预测问题,那么问题来了, 预测用这个算法怎么做。 主要有两种做法。

2.2.1 平均权重

每个testdata的预测选择前k个最小的distance,每个distance对应traindata的房价值,做平均值即为预测值,如下图

d0c4edb4ea753ffd3d78da213e764cf1.png

如果k为3则prediction = (7+6+2)/3 = 5

2.2.2 按权重大小来

按权重来, 即每个选中的distance作为总共distance总和的比例再乘以此distance对应的value最后求和即可,如下图

5ed343e90497379b52e857708f21b094.png

这里也有一种编程思想,即 如果不乘以7、6、2 剩下那部分其实就有点像前向推理里面的所谓的weights,后面的编程也会如此处理。

2.3 代码实现

top_k_value, top_k_index = tf.nn.top_k(tf.negative(distance), k=K)

top_k_value = tf.truediv(1.0, top_k_value)

top_k_value_sum = tf.reduce_sum(top_k_value, axis=1)
top_k_value_sum = tf.expand_dims(top_k_value_sum, 1)
top_k_value_sum_again = tf.matmul(top_k_value_sum, tf.ones([1, K], dtype=tf.float32))

top_k_weights = tf.div(top_k_value, top_k_value_sum_again)
weights = tf.expand_dims(top_k_weights, 1)

top_k_y = tf.gather(y_train_placeholder, top_k_index)
predictions = tf.squeeze(tf.matmul(weights, top_k_y), axis=[1])

这里的代码稍微有点绕,请随着我来一个个看 因为tf.topk是返回的前k个的最大的,这里我们要的distance是最小的,所以这里需要对distance来个negative操作也就是取负值

top_k_value, top_k_index = tf.nn.top_k(tf.negative(distance), k=K)

因为top_k_value变为负数了,所以这里用一个倒数就又可以恢复到以前的大小比较关系了,这里相当于一个数变为负的变小了,再倒数一下,如果所有数都这么干,那最后的大小关系还是之前正数的大小关系,因为我们不需要他的真正大小只需要 比例就可以了。倒数代码如下:

top_k_value = tf.truediv(1.0, top_k_value)

之后开始算整个总和,就可以和前面的公式比对来看,此过程相当于算5+8+3=16的过程

top_k_value_sum = tf.reduce_sum(top_k_value, axis=1)
top_k_value_sum = tf.expand_dims(top_k_value_sum, 1)

因为这里我们采用的都是矩阵方式,也就是很多操作需要矩阵化,前面步骤结束之后接下来应该是用每个distace的负数倒数来除以总和sum,但是在相除之前有一个步骤需要进行,相当于为之后的计算做铺垫,,每个sum扩展到k个在一个维度里。

top_k_value_sum_again = tf.matmul(top_k_value_sum, tf.ones([1, K], dtype=tf.float32))

最后相除

top_k_weights = tf.div(top_k_value, top_k_value_sum_again)
weights = tf.expand_dims(top_k_weights, 1)

之后取出前k个value,与整个权重weights进行矩阵相乘计算,前一步的expand_dims和之前仔细讲到的差不多功能,这里不赘述了。

top_k_y = tf.gather(y_train_placeholder, top_k_index)
predictions = tf.squeeze(tf.matmul(weights, top_k_y), axis=[1])

3.loss函数和session

loss = tf.reduce_mean(tf.square(tf.subtract(predictions, y_test_placeholder)))

loop_nums = int(np.ceil(len(x_test)/batchsize))

with tf.Session() as sess:
    for i in range(loop_nums):
        min_index = i*batchsize
        max_index = min((i+1)*batchsize, len(x_test))
        x_test_batch = x_test[min_index: max_index]
        y_test_batch = y_test[min_index: max_index]
        result, los = sess.run([predictions, loss], feed_dict={
            x_train_placeholder: x_train, y_train_placeholder: y_train,
            x_test_placeholder: x_test_batch, y_test_placeholder: y_test_batch
        })

        print("No.%d batch, loss is %f"%(i+1, los))

loss就为比较常见的计算loss的方式, 其他都是tf常用格式,问题不大。

4. 可视化

利用plt可视化柱状图hist

pins = np.linspace(5, 50, 45)
print(pins)
plt.hist(result, pins, alpha=0.5, label='prediction')
plt.hist(y_test_batch, pins, alpha=0.5, label='actual')
plt.legend(loc='best')
plt.show()

5. 全部code

import numpy as np
import re
import pandas as pd
import tensorflow as tf
from matplotlib import pyplot as plt
path = 'housing.data'
fr = open(path)
lines = fr.readlines()
dataset = []
for line in lines:
    line = line.strip()
    line = re.split(r' +', line)
    line = [float(i) for i in line]
    dataset.append(line)

housing_header = ['CRIM', 'ZN', 'INDUS', 'CHAS', 'NOX', 'RM', 'AGE', 'DIS', 'RAD', 'TAX', 'PTRATIO', 'B', 'LSTAT', 'MEDV']
cols_used = ['CRIM', 'INDUS', 'NOX', 'RM', 'AGE', 'DIS', 'TAX', 'PTRATIO', 'B', 'LSTAT']
dataset_array = np.array(dataset)
dataset_pd = pd.DataFrame(dataset_array, columns=housing_header)
housing_data = dataset_pd.get(cols_used)
labels = dataset_pd.get('MEDV')
housing_data = np.array(housing_data)
housing_data = (housing_data - housing_data.min(0))/housing_data.ptp(0)
labels = np.array(labels)[:, np.newaxis]

np.random.seed(13)
train_ratio = 0.8
data_size = len(housing_data)
train_index = np.random.choice(a=data_size, size=round(data_size*train_ratio), replace=False)
test_index = np.array(list(set(range(data_size))-set(train_index)))

x_train = housing_data[train_index]
y_train = labels[train_index]
x_test = housing_data[test_index]
y_test = labels[test_index]

num_features = len(cols_used)
batchsize = len(x_test)
K = 4

x_train_placeholder = tf.placeholder(tf.float32, [None, num_features])
x_test_placeholder = tf.placeholder(tf.float32, [None, num_features])

y_train_placeholder = tf.placeholder(tf.float32, [None, 1])
y_test_placeholder = tf.placeholder(tf.float32, [None, 1])

distance = tf.reduce_sum(tf.abs(tf.subtract(x_train_placeholder, tf.expand_dims(x_test_placeholder, 1))), axis=2)

# distance = tf.sqrt(tf.reduce_sum(tf.square(tf.subtract(x_train_placeholder, tf.expand_dims(x_test_placeholder, 1))), reduction_indices=[1]))
# x_test_placeholder_expansion = tf.expand_dims(x_test_placeholder, 1)
# x_substract = x_train_placeholder - x_test_placeholder_expansion
# x_square = tf.square(x_substract)
# x_square_sum = tf.reduce_sum(x_square, reduction_indices=[1])
# x_sqrt = tf.sqrt(x_square_sum)

top_k_value, top_k_index = tf.nn.top_k(tf.negative(distance), k=K)

top_k_value = tf.truediv(1.0, top_k_value)

top_k_value_sum = tf.reduce_sum(top_k_value, axis=1)
top_k_value_sum = tf.expand_dims(top_k_value_sum, 1)
top_k_value_sum_again = tf.matmul(top_k_value_sum, tf.ones([1, K], dtype=tf.float32))

top_k_weights = tf.div(top_k_value, top_k_value_sum_again)
weights = tf.expand_dims(top_k_weights, 1)

top_k_y = tf.gather(y_train_placeholder, top_k_index)
predictions = tf.squeeze(tf.matmul(weights, top_k_y), axis=[1])

loss = tf.reduce_mean(tf.square(tf.subtract(predictions, y_test_placeholder)))

loop_nums = int(np.ceil(len(x_test)/batchsize))

with tf.Session() as sess:
    for i in range(loop_nums):
        min_index = i*batchsize
        max_index = min((i+1)*batchsize, len(x_test))
        x_test_batch = x_test[min_index: max_index]
        y_test_batch = y_test[min_index: max_index]
        result, los = sess.run([predictions, loss], feed_dict={
            x_train_placeholder: x_train, y_train_placeholder: y_train,
            x_test_placeholder: x_test_batch, y_test_placeholder: y_test_batch
        })

        print("No.%d batch, loss is %f"%(i+1, los))

pins = np.linspace(5, 50, 45)
print(pins)
plt.hist(result, pins, alpha=0.5, label='prediction')
plt.hist(y_test_batch, pins, alpha=0.5, label='actual')
plt.legend(loc='best')
plt.show()

六、 结果

当batchsize为 测试数据大小的时候并且k=4

554719ea2bbbad844b78ae8fd69e06cd.png

8e2730bb3a2b1f879ce678bfc1052c86.png

七、 总结

  • 我们大多数利用KNN都是用分类问题,我们这里为预测方案
  • 我们利用tensorflow深度学习框思想去实现机器学习算法
  • 本实现主要是对KNN以及tensorflow的使用,最为关键是对于矩阵的计算和各种维度变换的更为深刻的使用和理解。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值