神经网络拟合曲线及讨论
神经网络拟合曲线及讨论
问题说明
神经网络能否拟合x^2 + y^2 = 100在第一象限的曲线?
设计思路
第一象限的曲线方程如下所示:
y=100−x2y = \sqrt{100-x^2} y=100−x2
在[0, 10]中等距生成1000个点,划分训练集、开发集和测试集,构建神经网络训练。神经网络架构图如下,采用两层神经网络进行拟合,根据情况调节神经网络的深度以及隐藏层神经元个数。
代码实现
import randomimport matplotlib.pyplot as plt
import numpy as np
import torch
from torch import nn
from torch.utils.data import Dataset, DataLoader
import torch.nn.functional as Fdef setup_seed(seed):# 固定所有的随机数种子,使结果能够复现torch.manual_seed(seed)torch.cuda.manual_seed_all(seed)np.random.seed(seed)random.seed(seed)torch.backends.cudnn.deterministic = Truedef generate_data(num=1000):x = np.linspace(0, 10, num)y = [np.sqrt(100 - i ** 2) for i in x]return list(zip(x, y))def distribute_dataset(data, rate):total_num = len(data)train_pos = int(total_num * rate[0])dev_pos = int(total_num * (rate[0] + rate[1]))train_data = data[: train_pos]dev_data = data[train_pos: dev_pos]test_data = data[dev_pos:]return train_data, dev_data, test_dataclass MyDataset(Dataset):# 继承torch工具类的Dataset,进而构造DataLoader进行批量数据读取# 继承Dataset需要实现以下3个函数def __init__(self, dataset):super().__init__()data_x, data_y = zip(*dataset)self.data_x = data_xself.data_y = data_ydef __getitem__(self, item):return self.data_x[item], self.data_y[item]def __len__(self):assert len(self.data_x) == len(self.data_y)return len(self.data_x)class MyModel(nn.Module):# 继承nn.Module构建模型def __init__(self, hidden_size):super(MyModel, self).__init__()self.h1 = nn.Linear(1, hidden_size)self.h2 = nn.Linear(hidden_size, 1)def forward(self, inputs):out1 = self.h1(inputs)out2 = self.h2(F.relu(out1))return out2if __name__ == '__main__':# 可调超参数data_num = 1000tdt_rate = [0.7, 0.2, 0.1]seed = 1hidden_size = 128batch_size = 4lr = 1e-3epoch = 50# 固定随机数setup_seed(seed)# 生成数据total_data = generate_data(data_num)random.shuffle(total_data)# 构建训练集、开发集、测试集train_data, dev_data, test_data = distribute_dataset(total_data, tdt_rate)train_ds = MyDataset(train_data)dev_ds = MyDataset(dev_data)test_ds = MyDataset(test_data)# 构建模型model = MyModel(hidden_size)loss = nn.MSELoss()optimizer = torch.optim.Adam(model.parameters(), lr=lr)# 训练集训练train_loader = DataLoader(train_ds, batch_size=batch_size)for e in range(epoch):total_l = []for x, y in train_loader:# 调整数据形式 x -> [batch_size, 1], y -> [batch_size]x = x.unsqueeze(-1).type(torch.float32)y = y.type(torch.float32)# 均方误差 (预测值 - 真实值)^2l = loss(model(x).squeeze(-1), y)# 邢将军法 |sign(预测值) * 预测值^2 - 真实值^2|# y_ = model(x).squeeze(-1)# sign = torch.sign(y_)# l = torch.abs(sign * y_ ** 2 - y ** 2).sum()# |预测值 - 真实值|# l = torch.abs(model(x).squeeze(-1) - y).sum()# |预测值^3 - 真实值^3|# l = torch.abs(model(x).squeeze(-1) ** 3 - y ** 3).sum()l.backward()optimizer.step()optimizer.zero_grad()total_l.append(l.item())print(f"epoch {e+1}, avg_loss {sum(total_l) / len(total_l)}")# for e in range(epoch):# # (x, y) -> (y, x) 增加靠近10的数据量,让模型再靠近10的地方拟合地更好# total_l = []# for x, y in train_loader:# x, y = y, x# # 调整数据形式 x -> [batch_size, 1], y -> [batch_size]# x = x.unsqueeze(-1).type(torch.float32)# y = y.type(torch.float32)## # 均方误差 (预测值 - 真实值)^2# # l = loss(model(x).squeeze(-1), y)## # 邢将军法 |sign(预测值) * 预测值^2 - 真实值^2|# y_ = model(x).squeeze(-1)# sign = torch.sign(y_)# l = torch.abs(sign * y_ ** 2 - y ** 2).sum()## # |预测值 - 真实值|# # l = torch.abs(model(x).squeeze(-1) - y).sum()## # |预测值^3 - 真实值^3|# # l = torch.abs(model(x).squeeze(-1) ** 3 - y ** 3).sum()## l.backward()# optimizer.step()# optimizer.zero_grad()# total_l.append(l.item())# print(f"reverse epoch {e+1}, avg_loss {sum(total_l) / len(total_l)}")# 开发集调超参数dev_loader = DataLoader(dev_ds, batch_size=batch_size)with torch.no_grad():xs, ys, y_s = [], [], []for x, y in dev_loader:xs.extend(list(x.numpy()))ys.extend(list(y.numpy()))x = x.unsqueeze(-1).type(torch.float32)y = y.type(torch.float32)y_ = model(x).squeeze(-1)y_s.extend(list(y_.numpy()))# 排序total = [(a, b, c) for a, b, c in zip(xs, ys, y_s)]total.sort(key=lambda i: i[0])xs, ys, y_s = zip(*total)plt.plot(xs, ys, label='real')plt.plot(xs, y_s, label='pred')plt.legend()plt.title('dev')plt.show()# 测试集测试test_loader = DataLoader(test_ds, batch_size=batch_size)with torch.no_grad():xs, ys, y_s = [], [], []for x, y in test_loader:xs.extend(list(x.numpy()))ys.extend(list(y.numpy()))x = x.unsqueeze(-1).type(torch.float32)y = y.type(torch.float32)y_ = model(x).squeeze(-1)y_s.extend(list(y_.numpy()))# 排序total = [(a, b, c) for a, b, c in zip(xs, ys, y_s)]total.sort(key=lambda i: i[0])xs, ys, y_s = zip(*total)plt.plot(xs, ys, label='real')plt.plot(xs, y_s, label='pred')plt.legend()plt.title('test')plt.show()
神经网络的学习能力很强,获得不错的拟合效果。
讨论
新的损失函数
上述代码中的loss选用的是均方误差——(预测值 - 真实值) ^2,邢将军提出了新的损失函数——|sign(预测值) * 预测值^2 - 真实值^2|,其中sign是符号函数,公式如下:
损失函数描述预测值和真实值之间的误差,当预测值趋近于真实值时,损失函数应越来越小。预测值^2 - 真实值^2 满足损失函数的基本定义。
损失函数不能为负数,网络的优化目标是最小化损失函数,如果损失可为负数,网络将把损失推向负无穷,所以加上绝对值,成为**|预测值^2 - 真实值^2|**。
在此次拟合中,真实值的取值范围是[0, 10],永为正数。当预测值趋近于真实值的相反数时,损失依然在不断减小。如果初始梯度方向是向着真实值相反数的方向,最终就会导致出现关于x轴对称的拟合曲线出现。所以当预测值趋近于真实值的相反数时,要让损失变大。这里使用sign函数,当预测值为负值时,将产生更大的损失。最终的损失函数为**|sign(预测值) * 预测值^2 - 真实值^2|**。
代码实现如下:
y_ = model(x).squeeze(-1)
sign = torch.sign(y_)
l = torch.abs(sign * y_ ** 2 - y ** 2).sum()
效果图如下,除了尾部数据仍然有偏差,拟合效果大幅提升。
基于邢将军的启发,我们可以设计更多损失函数,这些损失函数都表现不错。
- | 预测值 - 真实值|
- |sign(预测值) * 预测值^2 - 真实值^2|
- | 预测值^3 - 真实值^3|
- …
尾部偏差
sr同学认为预拟合的曲线在越靠近10的地方,|斜率|越来越大,最终极限为无穷大。在数据生成阶段,x是等距采样的,而y值的变化并非均匀。越靠近10,y值的变化越明显,特征越稀疏,不利于模型学习。
sr同学认为 x^2 + y^2 = 100 中,x与y是对称的,也就是说x与y可以互换。如果将生成的数据(x, y)对调成(y, x),则原来靠近0的地方数据更密集,现在靠近10的地方数据更密集。让模型再学习(y, x),尾部的偏差就能够拟合地更好。
代码很简单,将原训练代码的x与y互换即可。
for e in range(epoch):# (x, y) -> (y, x) 增加靠近10的数据量,让模型再靠近10的地方拟合地更好total_l = []for x, y in train_loader:# 对调x, y = y, x# 调整数据形式 x -> [batch_size, 1], y -> [batch_size]x = x.unsqueeze(-1).type(torch.float32)y = y.type(torch.float32)# 均方误差 (预测值 - 真实值)^2# l = loss(model(x).squeeze(-1), y)# 邢将军法 |sign(预测值) * 预测值^2 - 真实值^2|y_ = model(x).squeeze(-1)sign = torch.sign(y_)l = torch.abs(sign * y_ ** 2 - y ** 2).sum()# |预测值 - 真实值|# l = torch.abs(model(x).squeeze(-1) - y).sum()# |预测值^3 - 真实值^3|# l = torch.abs(model(x).squeeze(-1) ** 3 - y ** 3).sum()l.backward()optimizer.step()optimizer.zero_grad()total_l.append(l.item())print(f"reverse epoch {e+1}, avg_loss {sum(total_l) / len(total_l)}")
效果图如下,尾部数据也得到有效的拟合。
总结
- 经验上,对于回归问题,我们倾向于使用均方误差作为损失函数;对于分类问题,我们倾向于使用交叉熵作为损失函数。然而,我们可以根据实际问题,设计出更好的损失函数,让模型收敛更快,效果更好。
- 神经网络的学习能力很强,但需要大量数据多次训练。如果模型在某些地方表现的不够好,增加这方面的数据,模型就能自动学习到特征,表现得更好。
神经网络拟合曲线及讨论相关推荐
- 使用神经网络拟合曲线(MATLAB/Python)
前言 神经网络通常用于分类任务,也可以用于回归任务.使用一个含有隐层的神经网络可以很轻松地拟合出非线性曲线.下面是几个示例,包含matlab的和python的,都很简单. 实例1 首先,生成正弦曲线, ...
- 神经网络拟合是什么意思,神经网络拟合二次曲线
BP神经网络可以用于拟合函数吗 可以.既然是函数拟合,那么事先就已经有函数表达式了.拟合的只是函数表达式中未知的参数.用神经网络对函数进行拟合,输出的就是未知参数的高精近似值.人工神经网络就是模拟人思 ...
- 一文读懂神经网络(附解读案例)
"你的大脑并不产生思想.你的思想塑造了神经网络."--Deepak Chopra 引文 J. Nocedal y S. Wright, "Numerical optimi ...
- 独家 | 一文读懂神经网络(附解读案例)
作者:Matthew Stewart 翻译:车前子 校对:陈丹 本文约5500字,建议阅读12分钟. 本文的知识将提供一个强有力的基础,带你入门神经网络的性能,应用于深度学习应用. "你的大 ...
- CICC科普栏目|神经网络浅讲:从神经元到深度学习
图1 人脑神经网络 来源: 计算机的潜意识 摘要:神经网络是一门重要的机器学习技术.它是目前最为火热的研究方向–深度学习的基础. 神经网络是一门重要的机器学习技术.它是目前最为火热的研究方向–深度学习 ...
- 知物由学 | 未来安全隐患:AI的软肋——故意欺骗神经网络
本文由 网易云 发布. "知物由学"是网易云易盾打造的一个品牌栏目,词语出自汉·王充<论衡·实知>.人,能力有高下之分,学习才知道事物的道理,而后才有智慧,不去求问 ...
- grad在python什么模块_深度学习(Deep Learning)基础概念1:神经网络基础介绍及一层神经网络的python实现...
此专栏文章随时更新编辑,如果你看到的文章还没写完,那么多半是作者正在更新或者上一次没有更新完,请耐心等待,正常的频率是每天更新一篇文章. 该文章是"深度学习(Deep Learning)&q ...
- 第五章 卷积神经网络(CNN)
文章目录 5.1 卷积神经网络的组成层 5.2 卷积如何检测边缘信息? 5.3 卷积层中的几个基本参数? 5.3.1 卷积核大小 5.3.2 卷积核的步长 5.3.3 边缘填充 5.3.4 输入和输出 ...
- 卷积神经网络图像分类识别
卷积神经网络和图像分类识别Andrew Kirillov 著 Conmajia 译 2019 年 1 月 15 日原文发表于 CodeProject(2018 年 10 月 28 日). 中文版有小幅 ...
最新文章
- 基于Quartus II软件FPGA与PC之间的串行通信系统开发(9000+字)
- Spring Cloud Alibaba - 10 Ribbon 自定义负载均衡策略(权重算法)
- drtek收音机使用说明_一百年前的便携式矿石收音机长啥样?这台1919年产品给你答案...
- TypeScript的书写规范(TSLint)配置修改
- 关联规则java代码_重量挖掘关联规则挖掘方法,哪个大神可以将以下伪代码转换为Java代码?...
- LAMP源代码编译整理
- 【BZOJ3036】绿豆蛙的归宿 拓补排序+概率
- Criteria和DetachedCriteria区别应用
- 构造体中变量后面的冒号_类型在变量前面还是后面,终于有答案了
- C语言Dialogbox添加图片,c# dll c 类_dialogboxparam_msg结构
- Codeforces Round #512 (Div. 2) - D. Vasya and Triangle (皮克公式)
- 极简主义、人工智能与Readhub的产品哲学
- 10天背诵英文10000单词
- electron打开系统默认浏览器
- QT:黑白棋的吃子规则(七)
- android 播放wav代码,播放简短的.wav文件 - Android
- nagios 的安装
- 游戏开发与实现南梦宫笔记
- Charles软件破解方法
- Elasticsearch的这几个概念你还不知道啥意思呢?
热门文章
- 【笔记一】pycharm写入excel学习笔记
- MassGrid有奖征文活动
- OC-------包装类
- R语言which函数详解以及Rcpp改写
- 贵州茅台——病毒性营销方案
- 尚济 形意拳经解[七]内功经解
- 黑马Drools学习笔记(三)——Drools规则属性
- 【Linux进程概念——下】验证进程地址空间的基本排布 | 理解进程地址空间 | 进程地址空间如何映射至物理内存(页表的引出) | 为什么要存在进程地址空间 | Linux2.6内核进程调度队列
- 阿里云ECS安全增强通用型g7t服务器支持SGX加密计算由TPM/TCM芯片可信启动
- STM32F103标准固件库寄存器及常用函数整理