philm-iOS-wiki
  • 介绍
  • 网络层
    • 说明
  • UI
    • 说明
    • 在ios7以前使用ColorSpace的坑
    • UITableView偏移异常问题
    • drawRect时单独设置文字阴影无效
    • Xcode9下相册访问权限问题
    • 避免同时点击多个Button
    • scroll上的button延迟响应问题
    • uibutton触发边界事件
    • ios 11 上tableview 改动
    • YYImage 显示指定区域的图片
  • 数据持久化
    • 说明
  • 其它
    • 取消延迟执行之坑
    • NSString 转换 float 的精度问题
  • 每周阅读
    • 目录
    • 深入思考NSNotification
    • gitBook使用小助手
    • iOS App签名的原理
    • 响应链
    • iOS10跳转系统到设置页
    • SDWebImage下载高清图内存问题
    • iOS圆角避免离屏渲染
    • 常用的延时调用
    • iOS 神经网络
    • SDWebImage缓存策略
    • 3Dtouch
    • 为什么 Objective-C 对象存储在堆上而不是栈上
    • 深入浅出理解视频编码H264结构
    • CATextLayer学习
    • cocoaPods
    • 任意网站支持RSS
    • Metal简介
    • 动态更改icon
    • CAReplicatorLayer
    • 增加点击间隔
    • 勒索病毒当道的时代
    • iOS常用宏定义
    • Metal实现YUV转RGB渲染视频
    • 获取当前下载的app及下载进度
    • OpenGL ES 三种类型修饰 uniform attribute varying
    • 技术部门引入OKR
    • 基于runloop的线程保活、销毁与通信
    • 深入理解哈希表
    • TOLL-FREE BRIDGING 和 UNMANAGED
    • 开发者能拿到的标识符
    • Swift自定义LOG
    • 系统通知整理
    • iOS 中的 imageIO 与 image 解码
    • CGImageRef基本介绍及方法说明
    • Swift 3.0 语法
    • webview加载部分网页
    • 在CAAnimation中暂停动画
    • 把代码迁移到协调器上
    • ios11API更新整理
    • 非越狱iOS设备的远程控制实现原理
    • 关于本地化
    • swift命名空间
    • CoreML与coremltools体验
    • 力学动画
    • Swift 4官方文档中文版: The Basic(上)
    • swift 中的KVO用法
    • GPUImage的图像形变设计(简单形变部分)
    • iOS响应式架构
    • 移动端图片上传旋转、压缩的解决方案
    • AVFoundation使用指南AVAssert使用
    • 过渡动画
    • 谈谈 MVX 中的 Model
    • AVFoundation编程-AVPlayer使用
    • GPUImage的图像形变设计(复杂形变部分)
    • What's New in LLVM 9
    • ios的事件机制
    • GPUImage源码解读(一)
    • GPUImage源码解读(二)
    • iOS 启动优化
    • 模块化 Swift 中的状态
    • swift中的let和var背后的编程模式
    • Swift Runtime动态性分析
    • RAC下的响应式编程
    • GPUImage源码解读(三)
    • 如何准确判断webView是否加载完成
    • NSObject的+load和+initialize详解
    • ios8以后设置启动图
    • GPUImage源码解读(四)
    • Swift自动闭包
    • IOS11新特性
    • GPUImage源码解读(五)
    • 理解 OC 内部的消息调用、消息转发、类和对象
    • 修饰符
    • IOS 切面统计事件解耦
    • GPUImage源码解读(六)
    • CoreImage介绍
    • 影响Core Animation性能的原因
    • Instruments中的动画工具选项介绍
    • GPUImage源码解读(七)
    • Xcode 7新的特性Lightweight Generics 轻量级泛型与__kindof修饰符
    • GPUImage源码解读(八)
    • Core Image之自定 Filter
    • iOS通用链接
    • 谈nonatomic非线程安全问题
    • 深拷贝与浅拷贝
    • CIKernel 介绍
    • iOS11适配
    • GPUImage源码解读(九)
    • CVPixelBufferCreate使用的坑
    • ios一窥并发底层
    • ARKit进阶:物理世界
    • ARKit的工作原理及流程介绍
    • UI线程卡顿监控
    • FBKVOController使用
    • GPUImage源码解读(十)
    • WKWebView在ios11崩溃问题解决方法
    • 微信iOS SQLite源码优化实践
    • HEIF 和 HEVC 研究
    • 谈谈 iOS 中图片的解压缩
    • 提升 iOS 开发效率! Xcode 9 内置模拟器的9个技巧
    • ObjC和JavaScript的交互,在恰当的时机注入对象
    • iOS数据保护
    • iOS11中网络层的一些变化(Session707&709脱水版)
    • GPUImage源码解读(十一)
    • 一种避免 iOS 内存碎片的方法
    • pods的原理
    • GPUImage源码解读(十二)
    • GPUImage源码解读(十三)
    • iOS 11 Layout的新特性
    • iOS应用瘦身方法思路整理
    • GPUImage源码解读(十四)
    • CAEmitterLayer属性介绍
    • 浅析移动蜂窝网络的特点及其省电方案
    • 如何在 table view 中添加 3D Touch Peek & Pop 功能
    • iOS中锁的介绍与使用
    • NSLog效率低下的原因及尝试lldb断点打印Log
    • GPUImage源码解读(十五)
    • GPUImage源码解读(十六)
    • CADisplayLink
    • GPUImage源码解读(十七)
    • CADisplayLink
    • 老生常谈category增加属性的几种操作
    • 30行代码演示dispatch_once死锁
    • GPUImage源码解读(十八)
    • YYImage设计思路
    • GPUImage源码解读(十九)
    • 深入理解Tagged Pointer
    • iOS 11:WKWebView内容过滤规则详解
    • Swift语法对编译速度的影响
    • GPUImage源码解读(二十)
    • GPUImage源码解读(二十一)
    • iOS App间常用的五种通信方式
    • YYCache深入学习
    • 冲顶大会插件
    • iOS高性能图片架构与设计
    • YUV颜色编码解析
    • iOS传感器:App前后台切换后,获取敏感信息使用touch ID进行校验
    • GPUImage源码解读(二十二)
    • GPUImage源码解读(二十三)
    • 从零开始的机器学习 - Machine Learning(一)
    • 从零开始的机器学习 - Machine Learning(二)
    • GPUImage源码解读(二十四)
    • Objective-C消息转发机制
    • iOS 程序 main 函数之前发生了什么
    • MMKV--基于 mmap 的 iOS 高性能通用 key-value 组件
    • Objective-C 消息发送与转发机制原理
    • 谈Objective-C block的实现
    • GPUImage源码解读(二十五)
    • podfile语法
    • 轻量级低风险 iOS 热更新方案
    • 使用objection来模块化开发iOS项目
    • swift 中delegate的使用注意
    • 使用appledoc自动生成api文档
    • UITextChecker的使用
    • ARKit 如何给SCNNode贴Gif图片
    • Unity与iOS平台交互和原生插件开发
    • SceneKit编程珠玑
Powered by GitBook
On this page
  • 前言
  • 分类(Classification)
  • 正文
  • 逻辑回归LR(Logistic Regression)
  • Sigmoid
  • 为什么是Sigmoid?
  • 损失函数(Loss Function)
  • 矢量化编程
  • 凹函数
  • 逻辑回归的梯度下降法(Gradient Descent)
  • 过拟合问题
  • 正则化项(regularizer)
  • 实践
  • 结果
  1. 每周阅读

从零开始的机器学习 - Machine Learning(二)

Previous从零开始的机器学习 - Machine Learning(一)NextGPUImage源码解读(二十四)

Last updated 7 years ago

前言

继上一章 Re:从零开始的机器学习 - Machine Learning(一) 线性回归,我还是继续按照斯坦福的路线,这一章来说说逻辑回归(Logistic Regression)

分类(Classification)

和回归(Regression)一样,分类(Classification)问题也是机器学习里面很大的一块。

分类问题是机器学习非常重要的一个组成部分,它的目标是根据已知样本的某些特征,判断一个新的样本属于哪种已知的样本类。

其实常见的例子很多,判断一个邮件是否是垃圾邮件之类的,预测一个用户是否对我的商品感兴趣,以及图像处理里面对图像进行的分类。

图1

分类问题有简单的二分类也有多分类。

正文

这篇博客主要讲的是逻辑回归(Logistic regression)。

逻辑回归LR(Logistic Regression)

看到名字的时候你可能会有一些奇怪,为什么明明叫逻辑“回归”却用在分类问题上。虽然这个名字似乎指示着什么,但是逻辑回归实际上是分类算法。我猜它之所以这样命名是因为在它的学习方法上和线性回归相似,但是损失(loss)和梯度(gradient)函数的表达不同。特别的,逻辑回归使用 S型函数(sigmoid)而不是线性回归的连续输出。当我们深入到实现中去,我们会了解到更多。 首先我们先把逻辑回归放到一边,之前也说了逻辑回归是用来解决分类问题的,对于分类问题,我们实际上是希望得到一个分类器(Classifier),当输入数据之后,这个分类器能给我预测这个数据属于某一类的概率,也就是说我们需要的是一个概率。 上一节我们介绍的线性回归,其输出的是预测值,其假设函数(Hypothesis Function)也就是输出预测值的函数是这样的。

图2_2

而逻辑回归则是预测属于某一类的概率,所以我们让其假设函数是下面这个

这个函数的意义其实是当输入为x时输出y=1的概率,其实就是二分类问题里面是某个东西的概率。读者现在可能会对这个函数有所疑问,比如为什么是这个函数,这个留在后面会讨论。

这里出现了条件概率,实际上就是指事件A在另外一个事件B已经发生条件下的发生概率。高中生可以补这部分也可以暂时不补

我们把这个函数g(z)叫做sigmoid函数,很明显这个函数的值域是0到1的开区间。接下来我们会详细介绍一下这个函数。

Sigmoid

Sigmoid函数的函数表达式如下:

为什么是Sigmoid?

损失函数(Loss Function)

上一小节我们也说过,为了修正参数Θ我们需要有个手段来衡量当前参数Θ的优秀程度。损失函数(Loss Function)就是用来衡量假设函数(hypothesis function)的准确性。 对于逻辑回归来说,我们希望的是当预测概率约接近实际情况(0或1)的时候误差最小,而且不希望曲线是一条直线,而是对于越接近的地方变化越小,约远离的地方变化越大的函数。 下面就是逻辑回归的损失函数。

我们可以将函数合并一下,毕竟这种分段函数处理起来不是很舒服,其实就是下图这样,也很好理解,毕竟二分类训练数据y只有0和1两个值。

这样我们就可以算出在一个训练集中基于当前参数Θ得到结果的误差了。

矢量化编程

其实上一小节的实战中我们已经用到了矢量化编程。

# 计算损失,用了矢量化编程而不是for循环
def computeLoss(X, y, theta):
inner = np.power(((X * theta.T) - y), 2)
return np.sum(inner) / (2 * len(X))

矢量化编程是提高算法速度的一种有效方法。为了提升特定数值运算操作(如矩阵相乘、矩阵相加、矩阵-向量乘法等)的速度,数值计算和并行计算的研究人员已经努力了几十年。矢量化编程的思想就是尽量使用这些被高度优化的数值运算操作来实现我们的学习算法。 换句话说就是尽量避免使用for循环,毕竟矩阵相乘这种场景非常适合并行计算,在巨量的数据面前性能收益非常明显。 如果刚刚的损失函数用矢量化编程的思想来表示的话

如果一时不理解的话我先解释一下,我们先假设共m个数据,而这个模型中变量有n个。则矩阵h就是(m, n) X (n, 1)也就是(m,1)矩阵,矩阵h的意义就是这m个数据的预测值。 损失函数中y的转置为(1, m),相乘后得到(1, 1)也就是一个值,这两个矩阵相乘的意义则是对应的预测值取对数乘以对应的实际值,最后加在一起。

(m,n)表示维度为m行n列的矩阵,如果学过矩阵的乘法应该知道矩阵相乘(m, n) X (n, k)得到的矩阵是(m, k)

凹函数

逻辑回归的梯度下降法(Gradient Descent)

关于梯度下降法我在上一小节里面已经做了比较具体的描述,如果忘记了可以回去翻翻。我们刚刚知道了怎么评价当前参数Θ的好坏,现在我们需要做的是使用梯度下降法来调整参数。

依旧是对损失函数求偏导数,别忘记α是学习速率的意思。

矢量化表示为

损失函数偏导数求解过程:

过拟合问题

对于一个训练数据集,可视化后如下图所示。

对于三个不同的分类器划分出的边界的三种情况,我们对其有不同的称呼

第一种,分类非常不准,这种我们叫欠拟合(underfitting) 第二种,分类得恰到好处,这种其实没有特别的称呼。 第三种,分类太过于契合训练数据了。这种我们称为过拟合(overfitting) 过拟合所产生的问题也很明显,它实在太过于契合训练集了,对于我们来说,第二个曲线才是我们想要的,过拟合的结果太过于契合训练数据,实用性可想而知的低。 而解决过拟合的方法主要有两种 减少特征的数量,这个很好理解,更多的特征意味着划分出来的函数曲线可以越复杂。这个可以扩展到以后会讲的特征工程(Feature Engineering) 使用正则化项, 保持所有的特征,但是保证参数θj不会变得巨大。正则化项非常适合在我们拥有很多稍微有点用的特征的时候。

正则化项(regularizer)

正则化项其实也叫惩罚项(penalty term),其作用是减缓过拟合问题,其实就是在损失函数后面加一个含有各个Θ的项,这样做的目的是让Θ也参与损失函数的计算,这样由于我们需要求的是损失函数的最小值,这个项就会限制Θ的大小。 这个正则化项的目的其实是一个权衡,我们即希望参数Θ能在训练数据集上表现得比较好,又不希望参数Θ训练出来的值非常大而产生一些奇葩的划分曲线,就像下图这样的。

实践

环境

如果你不想被配环境烦死的话,我真的推荐装Anaconda,除此之外要说的就是我用的都是Python3.x。

背景

本篇的实战主要是利用逻辑回归来帮助招生办筛选申请人。假设你想要基于两次考试的结果预测每个申请人是否会被录取。你有之前历史申请人的历史数据,包括两次考试的分数以及最后他们是否被录取。为了完此目的,我们将使用逻辑回归建立一个基于考试分数的分类模型(classification model )以估计录取的可能性。

代码及注释

import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

def sigmoid(z):
return 1 / (1 + np.exp(-z))

# 读入训练数据
# windows用户路径可能需要修改下,后期有时间可能会做统一
def loadData(path):
trainingData = pd.read_csv(path, header=None, names=['Exam 1', 'Exam 2', 'Admitted'])

trainingData.head()

trainingData.describe()

positive = trainingData[trainingData['Admitted'].isin([1])]
negative = trainingData[trainingData['Admitted'].isin([0])]

fig, ax = plt.subplots(figsize=(12,8))
ax.scatter(positive['Exam 1'], positive['Exam 2'], s=50, c='b', marker='o', label='Admitted')
ax.scatter(negative['Exam 1'], negative['Exam 2'], s=50, c='r', marker='x', label='Not Admitted')
ax.legend()
ax.set_xlabel('Exam 1 Score')
ax.set_ylabel('Exam 2 Score')
# plt.show()
return trainingData

# 计算损失,用了矢量化编程而不是for循环,公式在博客中有详细描述和证明。
def computeLoss(X, y, theta):
theta = np.copy(theta)
X = np.copy(X)
y = np.copy(y)
m = X.shape[0]
h = sigmoid(np.matmul(X, theta.T))
first = np.matmul(-(y.T), np.log(h))
second = np.matmul((1 - y).T, np.log(1 - h))
return np.sum(first - second) / m

# 梯度下降部分
def gradientDescent(X, y, theta, alpha, iters):
m = X.shape[0] # 数据项数m
temp = np.matrix(np.zeros(theta.shape))
# parameters = 1
cost = np.zeros(iters)

for i in range(iters):
error = sigmoid(np.matmul(X, theta.T)) - y


theta = theta - ((alpha/m) * np.matmul(X.T, error)).T
cost[i] = computeLoss(X, y, theta)

return theta, cost

def predict(theta, X):
probability = sigmoid(np.matmul(X, theta.T))
return [1 if x >= 0.5 else 0 for x in probability]

trainingData = loadData(os.getcwd() + '/../../data/ex2data1.txt')

# 插入常数项
trainingData.insert(0, 'Ones', 1)

cols = trainingData.shape[1]
X = trainingData.iloc[:,0:cols-1]
y = trainingData.iloc[:,cols-1:cols]

# 初始化X、Y以及theta矩阵
X = np.matrix(X.values)
y = np.matrix(y.values)
theta = np.matrix(np.zeros(3))

# 计算训练前的损失值
computeLoss(X, y, theta)

# 使用梯度下降得到模型参数
alpha = 0.001
iters = 20000
theta_fin, loss = gradientDescent(X, y, theta, alpha, iters)


# 计算训练后的参数的损失值 (不优化)
computeLoss(X, y, theta_fin)  #


# 损失随着迭代次数的变化 (不优化)
# fig, ax = plt.subplots(figsize=(12,8))
# ax.plot(np.arange(iters), loss, 'r')
# ax.set_xlabel('Iterations')
# ax.set_ylabel('Cost')
# ax.set_title('Error vs. Training Epoch')
# plt.show()

# 不理解为什么不优化的会这么低,是学习速率没动态变化么?
predictions = predict(theta_fin, X)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
accuracy = (sum(map(int, correct)) % len(correct))
print('accuracy 1 = {0}%'.format(accuracy)) # 60%


# 使用scipy的optimize来做优化
import scipy.optimize as opt
# 换了下参数位置让其符合fmin_tnc
def gradient(theta, X, y):
theta = np.matrix(theta)
X = np.matrix(X)
y = np.matrix(y)

parameters = int(theta.ravel().shape[1])
grad = np.zeros(parameters)

error = sigmoid(X * theta.T) - y

for i in range(parameters):
term = np.multiply(error, X[:,i])
grad[i] = np.sum(term) / len(X)

return grad
# 换了下参数位置让其符合fmin_tnc
def computeLoss2(theta, X, y):
theta = np.copy(theta)
X = np.copy(X)
y = np.copy(y)
m = X.shape[0]
h = sigmoid(np.matmul(X, theta.T))
first = np.matmul(-(y.T), np.log(h))
second = np.matmul((1 - y).T, np.log(1 - h))
return np.sum(first - second) / m
result = opt.fmin_tnc(func=computeLoss2, x0=theta, fprime=gradient, args=(X, y))

predictions = predict(np.matrix(result[0]), X)
correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
accuracy = (sum(map(int, correct)) % len(correct))
print('accuracy 2 = {0}%'.format(accuracy)) # 89%

结果

图2_3

图2_4

图2_5

sigmoid函数图像: 图2_6

图2_7

图2_8

图2_9

图2_10

图2_11

图2_12

图2_13

图2_14

图2_15

图2_16

图2_17

图2_18

在这篇文章中我们将目标从预测连续值的回归问题转到将结果进行分类的分类问题。 数据都可以在我的GitHub库上下载到。 github 地址:

数据可视化: 图2_19

训练集中正确率: 图2_20

原文链接:

https://github.com/lwyj123/re-machine-learning
https://juejin.im/post/5a40e8e46fb9a0450671dd2f