草庐IT

20210607 TensorFlow 实现 Logistic 回归

ATaburiss 2023-03-28 原文
0-1 导包

import warnings warnings.filterwarnings("ignore") import numpy as np import tensorflow as tf import matplotlib.pyplot as plt
1-1 构造数据

np.random.seed(999) raw_data = np.random.standard_normal((1000, 2)) # 产生 0 均值的 正态分布 plt.figure(figsize=(8,6))       # 设置当前图像的长和宽 plt.scatter(raw_data[:,0],raw_data[:,1],marker='o') plt.grid() plt.xlabel('x1') plt.ylabel('x2') plt.show()
1-1-2 现在定义一条直线,进行二分类

def pre(x):     return -2.5 * x  - 0.5 # 横轴是 x1 纵轴是 x2 x1 = np.linspace(-2, 2, 500)    # 在 -2 到 2 之间,取出 500 个数 plt.figure(figsize=(8,6)) plt.plot(x1, pre(x1),'salmon') plt.scatter(raw_data[:,0],raw_data[:,1],marker='o') plt.grid() plt.xlabel('x1') plt.ylabel('x2') plt.show()
1-1-3 现在想区分数据

将x1的值代入直线方程,如果 x2 值,小于当前线上的值,就是直线下方的类

new_raw_data_0 = [] new_raw_data_1 = [] for one_data in raw_data:     x1 = one_data[0]     x2 = one_data[1]     if x2 > pre(x1):         tag = 1         new_raw_data_1.append([x1,x2,tag])     else:         tag = 0         new_raw_data_0.append([x1,x2,tag])
1-1-4 对二分类添加噪声

def add_noise(x):     x = x + np.random.random(1)[0] - 0.5     return x new_raw_data_0 = [[add_noise(i[0]),add_noise(i[1]),i[2]] for i in new_raw_data_0] new_raw_data_1 = [[add_noise(i[0]),add_noise(i[1]),i[2]] for i in new_raw_data_1] new_raw_data_0 = np.array(new_raw_data_0) new_raw_data_1 = np.array(new_raw_data_1) # 列表生成式的方式添加噪声,然后变成 numpy 的数据格式
1-1-5 拿到数值后,画图

x1 = np.linspace(-2, 2, 500) plt.figure(figsize=(8,6)) plt.plot(x1, pre(x1),'salmon') plt.scatter(new_raw_data_0[:,0],new_raw_data_0[:,1],marker='o') plt.scatter(new_raw_data_1[:,0],new_raw_data_1[:,1],marker='o') plt.grid() plt.xlabel('x1') plt.ylabel('x2') plt.show()
2-1 数据处理

# all data all_data = np.concatenate([new_raw_data_0,new_raw_data_1])  # 拼接时,默认 axis = 0 # 现在,这样的数据是不可以放到神经网络中进行训练的 # 前面全是 0,后面全是 1,这样 loss 波动非常大,是不合理的 # 所以需要打乱顺序,使用 np.random.shuffle 将数据打乱,将数据丢进去,不需要返回值,all_data 会被修改,直接内部打乱 np.random.shuffle(all_data)   # 然后将数据分出 训练集 和 测试集 train_data = all_data[:-64] test_data = all_data[-64:]
2-2 数据分块

# 生成器 def gen_batch(data):     np.random.shuffle(data)     for i in range(len(data) // 64):         cursor = 64 * i         batch_data = data[cursor : cursor + 64]         # 切块时,第一个 维度全要,第二个维度 有 3 个 数,取出前 2 个数         x = batch_data[:, 0:2]           # y 只取 标签,0 或者 1         y = batch_data[:, 2]         yield x,y.reshape(-1,1)
2-2-1 运行生成器,查看结果

for x_,y_ in gen_batch(train_data):     print(x_.shape)     print(y_.shape)     print('-------')     break-->
(64, 2)
(64, 1)
-------
结果和预期一致

3-1 超参数

learing_rate = 0.01    # 梯度下降的学习率,一般都会传 0.01 num_train_epochs = 500  # 训练 500 次  display_per_step = 100
3-2 计算图

graph = tf.Graph()  # 定义一个图 with graph.as_default():    # 默认图     # None 代表第一维度不论是多少,都可以接收;分 64 块就是64,分 128 块就是128     x = tf.placeholder(shape=[None,2], dtype=tf.float32, name='x')     y = tf.placeholder(shape=[None,1], dtype=tf.float32, name='y')     # w 设置了全 0,形状是(2,1),因为 x1 x2 分别乘以 2 个 w     # 当然,不建议全 0 初始化,全 0 初始化存在问题,     # 因为这里的权重数量比较少,所以全0初始化问题不大     w =  tf.Variable(tf.ones(shape=[2,1]), dtype=tf.float32)     b =  tf.Variable(0, dtype=tf.float32)     logits = tf.matmul(x, w) + b    # 矩阵运算,x乘以w, logits 是线性输出的结果     # 逻辑回归,就是线性输出,套一个非线性函数,因为非线性函数可以把这些值映射到 0-1 之间     # 可以以 0.5 作为边界,判断 0 或者 1     y_pred = tf.sigmoid(logits)     # 将线性输出结果 logits 传进 非线性函数 sigmoid     # 下一步,对y的真实值和预测值,做交叉熵     # 定义loss,这里调用了 TensorFlow 的交叉熵函数,这里使用的是 sigmoid 交叉熵函数     # tf.nn.sigmoid_cross_entropy_with_logits,传入了两个数,一个是标签,一个是 logits 线性输出结果     # 当前的sigmoid交叉熵 会 自动进行 sigmoid 非线性变换,不需要 把已经变换完的 y_pred 传进来     # 一定要传入 线性输出结果 logits,得到的交叉熵是 64 个结果,所以需要 reduce_mean,做平均     loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=logits), name="calculate_loss")     # 定义优化器     optimizer = tf.train.GradientDescentOptimizer(learing_rate)      train_step = optimizer.minimize(loss)
3-3 运行计算图

with tf.Session(graph=graph) as sess:     init = tf.global_variables_initializer()     sess.run(init)     step = 0     for epoch in range(num_train_epochs):            for x_, y_ in gen_batch(train_data):             step += 1             _, l = sess.run([train_step, loss], feed_dict={x: x_, y: y_})             if step % display_per_step == 0:                 print("step: {:>4}, loss: {:.4}".format(step, l))     print('training over')     # 下面的是 测试集上的 loss     x_test,y_test = next(gen_batch(test_data))     loss_test = sess.run(loss,feed_dict={x: x_test, y: y_test})     print("test loss is {:.4}".format(loss_test))     res_weights = sess.run([w, b])     print(res_weights)-->
step:  100, loss: 0.3349
……
step: 7000, loss: 0.2156
training over
test loss is 0.2402
[array([[3.6807547],
       [1.3876256]], dtype=float32), 0.7486744]
# loss 是在逐步下降中,这里的w1 w2 和 b 值 和 前面的 wx+b的w和b 是不一样的
# 二分类后,可以添加评价指标,之前只看 loss,现在可以看除 loss 之外的 其他东西
# 0 1 标签是可以计算正确率的,线性回归中不涉及,因为线性回归中只是系数近似

4-1 添加正确率指标

graph = tf.Graph() with graph.as_default():     x = tf.placeholder(shape=[None,2], dtype=tf.float32, name='x')     y = tf.placeholder(shape=[None,1], dtype=tf.float32, name='y')     w =  tf.Variable(tf.ones(shape=[2,1]), dtype=tf.float32)     b =  tf.Variable(0, dtype=tf.float32)     logits = tf.matmul(x, w) + b     y_pred = tf.sigmoid(logits)         # 命名空间,目的是收集权重;     # 把某一块放到命名空间下,操作比较方便,计算图看起来更结构化一些     # 定义loss     with tf.name_scope("loss"):              loss = tf.reduce_mean(tf.nn.sigmoid_cross_entropy_with_logits(labels=y, logits=logits), name="calculate_loss")     # 定义优化器     with tf.name_scope("SGD"):         optimizer = tf.train.GradientDescentOptimizer(learing_rate)         train_step = optimizer.minimize(loss)     # 定义正确率     with tf.name_scope("calculate_accuracy"):         res_pred = tf.cast(tf.greater_equal(y_pred, 0.5), dtype=tf.float32)              # 判断序列中的值 是否大于等于  0.5,是 返回 True,否 返回 False;tf.cast 强制转换成 0 1         # 所以 res_pred 是一堆 0 1 值         acc = tf.reduce_mean(tf.cast(tf.equal(res_pred, y), dtype=tf.float32))         # 对应位置 一一判断 是否相等,对应下标的每一个 1 0 是否相等 with tf.Session(graph=graph) as sess:     init = tf.global_variables_initializer()     sess.run(init)     step = 0     for epoch in range(num_train_epochs):         for x_, y_ in gen_batch(train_data):             step += 1             _, l, acc_ = sess.run([train_step, loss, acc], feed_dict={x: x_, y: y_})             if step % display_per_step == 0:                 print("step: {:>4}, loss: {:.4}, acc: {:.4%}".format(step, l, acc_))     print('training over')     x_test,y_test = next(gen_batch(test_data))     loss_test, acc_test = sess.run([loss, acc],feed_dict={x: x_test, y: y_test})     print("test loss is {:.4}, acc is {:.4%}".format(loss_test, acc_test))     res_weights = sess.run([w, b])     print(res_weights)-->
step:  100, loss: 0.3412, acc: 85.9375%
step:  200, loss: 0.3945, acc: 82.8125%
……
step: 6900, loss: 0.2757, acc: 87.5000%
step: 7000, loss: 0.1816, acc: 93.7500%
training over
test loss is 0.2401, acc is 90.6250%
[array([[3.6801553],
       [1.3892566]], dtype=float32), 0.7487771]
# 正确率在震荡中,不断上升;测试集的 loss 是0.24,正确率约 92%
# 现在 逻辑回归 基本上就训练完了

5-1 画出二分类的线
# 思考过程
# 正向过程
# y_pred = sigmoid(w1*x1+w2*x2+b)
# if y_pred>=0.5 res_pred=1.0
# if y_pred<0.5 res_pred=0.0
# w1*x1+w2*x2+b = 0   y_pred 等于 0.5 的那条线;这是分界线
# x2 = (-w1*x1-b)/w2  以 x2 作为 y 值,所以画这条线即可
# w1*x1+w2*x2+b >= 0
# x2 >= (-w1*x1-b)/w2

def formula(x1, w1, w2, b):     x2 = (-w1*x1-b)/w2     return x2 plt.figure(figsize=(8,6)) line_space = np.linspace(-2, 2, 1024) plt.scatter(new_raw_data_0[:,0],new_raw_data_0[:,1],marker='o') plt.scatter(new_raw_data_1[:,0],new_raw_data_1[:,1],marker='o') w1 = res_weights[0][0][0] w2 = res_weights[0][1][0] b = res_weights[1] plt.plot(line_space, formula(line_space, w1, w2, b)) plt.grid() plt.show()
部分理论说明:
https://blog.51cto.com/u_15149862/2875921
1. 如何参数初始化?为什么不建议全 0 初始化?
2. 什么是 Logistic 回归?
3. 什么是激活函数?激活函数都有哪些?
4. 正确率如何计算?除了正确率还有什么评价指标?

有关20210607 TensorFlow 实现 Logistic 回归的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  3. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  4. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  5. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  6. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  7. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

  8. ruby - 实现k最近邻需要哪些数据? - 2

    我目前有一个reddit克隆类型的网站。我正在尝试根据我的用户之前喜欢的帖子推荐帖子。看起来K最近邻或k均值是执行此操作的最佳方法。我似乎无法理解如何实际实现它。我看过一些数学公式(例如k表示维基百科页面),但它们对我来说并没有真正意义。有人可以推荐一些伪代码,或者可以查看的地方,以便我更好地了解如何执行此操作吗? 最佳答案 K最近邻(又名KNN)是一种分类算法。基本上,您采用包含N个项目的训练组并对它们进行分类。如何对它们进行分类完全取决于您的数据,以及您认为该数据的重要分类特征是什么。在您的示例中,这可能是帖子类别、谁发布了该项

  9. ruby-on-rails - 使用 Ruby 正确处理 Stripe 错误和异常以实现一次性收费 - 2

    我查看了Stripedocumentationonerrors,但我仍然无法正确处理/重定向这些错误。基本上无论发生什么,我都希望他们返回到edit操作(通过edit_profile_path)并向他们显示一条消息(无论成功与否)。我在edit操作上有一个表单,它可以POST到update操作。使用有效的信用卡可以正常工作(费用在Stripe仪表板中)。我正在使用Stripe.js。classExtrasController5000,#amountincents:currency=>"usd",:card=>token,:description=>current_user.email)

  10. ruby - Ruby 1.8 的 Shellwords.shellescape 实现 - 2

    虽然1.8.7的构建我似乎有一个向后移植的Shellwords::shellescape版本,但我知道该方法是1.9的一个特性,在1.8的早期版本中绝对不支持.有谁知道我在哪里可以找到(以Gem形式或仅作为片段)针对Ruby转义的Bourne-shell命令的强大独立实现? 最佳答案 您也可以从shellwords.rb中复制您想要的内容。在Ruby的颠覆存储库的主干中(即GPLv2'd):defshellescape(str)#Anemptyargumentwillbeskipped,soreturnemptyquotes.ret

随机推荐