Pytorch 深度学习 Day01 ---线性回归

线性回归实战
部署运行你感兴趣的模型镜像

线性回归

主要内容包括:

1.线性回归的基本要素
2.线性回归模型从零开始的实现
3.线性回归模型使用pytorch的简洁实现

线性回归的基本要素

模型

为了简单起见,这里我们假设价格只取决于房屋状况的两个因素,即面积(平方米)和房龄(年)。接下来我们希望探索价格与这两个因素的具体关系。线性回归假设输出与各个输入之间是线性关系:在这里插入图片描述

数据集

我们通常收集一系列的真实数据,例如多栋房屋的真实售出价格和它们对应的面积和房龄。我们希望在这个数据上面寻找模型参数来使模型的预测价格与真实价格的误差最小。在机器学习术语里,该数据集被称为训练数据集(training data set)或训练集(training set),一栋房屋被称为一个样本(sample),其真实售出价格叫作标签(label),用来预测标签的两个因素叫作特征(feature)。特征用来表征样本的特点。

损失函数

在模型训练中,我们需要衡量价格预测值与真实值之间的误差。通常我们会选取一个非负数作为误差,且数值越小表示误差越小。一个常用的选择是平方函数。 它在评估索引为 i 的样本误差的表达式为:在这里插入图片描述

优化函数 - 随机梯度下降

当模型和损失函数形式较为简单时,上面的误差最小化问题的解可以直接用公式表达出来。这类解叫作解析解(analytical solution)。本节使用的线性回归和平方误差刚好属于这个范畴。然而,大多数深度学习模型并没有解析解,只能通过优化算法有限次迭代模型参数来尽可能降低损失函数的值。这类解叫作数值解(numerical solution)。

在求数值解的优化算法中,小批量随机梯度下降(mini-batch stochastic gradient descent)在深度学习中被广泛使用。它的算法很简单:先选取一组模型参数的初始值,如随机选取;接下来对参数进行多次迭代,使每次迭代都可能降低损失函数的值。在每次迭代中,先随机均匀采样一个由固定数目训练数据样本所组成的小批量(mini-batch) B ,然后求小批量中数据样本的平均损失有关模型参数的导数(梯度),最后用此结果与预先设定的一个正数的乘积作为模型参数在本次迭代的减小量。
在这里插入图片描述
在这里插入图片描述

矢量计算

在模型训练或预测时,我们常常会同时处理多个数据样本并用到矢量计算。在介绍线性回归的矢量计算表达式之前,让我们先考虑对两个向量相加的两种方法。

1.向量相加的一种方法是,将这两个向量按元素逐一做标量加法。
2.向量相加的另一种方法是,将这两个向量直接做矢量加法。

import torch
import time
#init variable a, b as 1000 dimension vector
n = 1000
#ones(*sizes)  全是1的tensor
a = torch.ones(n)
a = torch.ones(n)
# define a timer class to record time
class Timer(object):
    """Record multiple running times."""
    def __init__(self): # 类似于c++中的默认构造函数
        self.times = []
        self.start()

    def start(self):
        # start the timer
        self.start_time = time.time()

    def stop(self):
        # stop the timer and record time into a list
        self.times.append(time.time() - self.start_time)
        return self.times[-1]

    def avg(self):
        # calculate the average and return
        return sum(self.times)/len(self.times)

    def sum(self):
        # return the sum of recorded time
        return sum(self.times)

def init(self) 这种形式在__init__方法中,只有一个self,指的是实例的本身,但是在方法的类部,包含两个属性,time和star函数。它允许定义一个空的结构,当新数据来时,可以直接添加。实例化时,需要实例化之后,再进行赋值。

现在我们可以来测试了。首先将两个向量使用for循环按元素逐一做标量加法。

timer = Timer()
c = torch.zeros(n)
for i in range(n):
    c[i] = a[i] + b[i]
'%.5f sec' % timer.stop()

另外是使用torch来将两个向量直接做矢量加法:

timer.start()
d = a + b
'%.5f sec' % timer.stop()

线性回归模型从零开始的实现

# import packages and modules
%matplotlib inline
import torch
from IPython import display
from matplotlib import pyplot as plt
import numpy as np
import random
print(torch.__version__)

生成数据集

使用线性模型来生成数据集,生成一个1000个样本的数据集,下面是用来生成数据的线性关系:
在这里插入图片描述

# set input feature number 
num_inputs = 2
# set example number
num_examples = 1000
# set true weight and bias in order to generate corresponded label
true_w = [2, -3.4]
true_b = 4.2
#随机填充1000个输出为2,填充数据类型为float的tensor
features = torch.randn(num_examples, num_inputs, dtype=torch.float32)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float32)

使用图像来展示生成的数据

# 后面的1是点的大小
plt.scatter(features[:, 1].numpy(), labels.numpy(), 1);

读取数据集

定义一个函数

def data_iter(batch_size, features, labels):
    num_examples = len(features)
    indices = list(range(num_examples))
    random.shuffle(indices)  # random read 10 samples
    for i in range(0, num_examples, batch_size):
        j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # the last time may be not enough for a whole batch
        yield  features.index_select(0, j), labels.index_select(0, j)

shuffle() 方法将序列的所有元素随机排序,shuffle() 是不能直接访问的,需要导入 random 模块,然后通过 random 静态对象调用该方法。
yield 简介

batch_size = 10
for X, y in data_iter(batch_size, features, labels):
    print(X, '\n', y)
    break

初始化模型参数

w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float32)
#生成一个数据类型为float32的张量
b = torch.zeros(1, dtype=torch.float32)
w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)

np.random.normal(0, 0.01, (num_inputs, 1) 随机生成数据类型为float32正态分布分布均值为0,标准差为0.01,输出的值赋在(num_inputs, 1),默认为None,只输出一个值
如果将属性.requires_grad设置为True,他将开始追踪再其上的所有操作,就可以利用链式法则进行梯度传播,完成计算后还可以用.backward()来完成所有梯度计算。

定义模型

在这里插入图片描述

# torch.mm(a, b)是矩阵a和b矩阵相乘
def linreg(X, w, b):
    return torch.mm(X, w) + b

在这里插入图片描述

# 用view()改变Tensor的形状
def squared_loss(y_hat, y): 
    return (y_hat - y.view(y_hat.size())) ** 2 / 2

在这里插入图片描述

def sgd(params, lr, batch_size): 
    for param in params:
        param.data -= lr * param.grad / batch_size # ues .data to operate param without gradient track

在这里插入图片描述

# super parameters init
lr = 0.03
num_epochs = 5

net = linreg
loss = squared_loss

# training
for epoch in range(num_epochs):  # training repeats num_epochs times
    # in each epoch, all the samples in dataset will be used once
    # X is the feature and y is the label of a batch sample
    for X, y in data_iter(batch_size, features, labels):
        l = loss(net(X, w, b), y).sum()  
        # calculate the gradient of batch sample loss 
        l.backward()  
        # using small batch random gradient descent to iter model parameters
        sgd([w, b], lr, batch_size)  
        # reset parameter gradient,每次一个轮回后要重置梯度
        w.grad.data.zero_()
        b.grad.data.zero_()
    train_l = loss(net(features, w, b), labels)
    print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))

线性回归模型使用pytorch的简洁实现

import torch
from torch import nn
import numpy as np
torch.manual_seed(1)

print(torch.__version__)
torch.set_default_tensor_type('torch.FloatTensor')

在这里插入图片描述

num_examples = 1000

true_w = [2, -3.4]
true_b = 4.2

features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype=torch.float)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)

读数据集

import torch.utils.data as Data

batch_size = 10

# combine featues and labels of dataset
dataset = Data.TensorDataset(features, labels)
# put dataset into DataLoader
data_iter = Data.DataLoader(
    dataset=dataset,            # torch TensorDataset format
    batch_size=batch_size,      # mini batch size
    shuffle=True,               # whether shuffle the data or not
    num_workers=2,              # read data in multithreading
)
for X, y in data_iter:
    print(X, '\n', y)
    break

定义模型

class LinearNet(nn.Module):
    def __init__(self, n_feature):
        super(LinearNet, self).__init__()      # call father function to init 
        self.linear = nn.Linear(n_feature, 1)  # function prototype: `torch.nn.Linear(in_features, out_features, bias=True)`

    def forward(self, x):
        y = self.linear(x)
        return y
    
net = LinearNet(num_inputs)
print(net)
# ways to init a multilayer network
# method one 一个有序的容器,神经网络模块将按照在传入构造器的顺序依次被添加到计算图中执行,同时以神经网络模块为元素的有序字典也可以作为传入参数。
net = nn.Sequential(
    nn.Linear(num_inputs, 1)
    # other layers can be added here
    )
# method two
net = nn.Sequential()
net.add_module('linear', nn.Linear(num_inputs, 1))
# net.add_module ......

# method three
from collections import OrderedDict
net = nn.Sequential(OrderedDict([
          ('linear', nn.Linear(num_inputs, 1))
          # ......
        ]))

print(net)
print(net[0])

初始化模型参数

from torch.nn import init

init.normal_(net[0].weight, mean=0.0, std=0.01)
init.constant_(net[0].bias, val=0.0)  # or you can use `net[0].bias.data.fill_(0)` to modify it directly
for param in net.parameters():
    print(param)

定义损失函数

loss = nn.MSELoss()    # nn built-in squared loss function
                       # function prototype: `torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')`

定义优化函数

import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.03)   # built-in random gradient descent function
print(optimizer)  # function prototype: `torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)`

训练

num_epochs = 3
for epoch in range(1, num_epochs + 1):
    for X, y in data_iter:
        output = net(X)
        l = loss(output, y.view(-1, 1))
        optimizer.zero_grad() # reset gradient, equal to net.zero_grad()
        l.backward()
        optimizer.step()
    print('epoch %d, loss: %f' % (epoch, l.item()))
num_epochs = 3
for epoch in range(1, num_epochs + 1):
    for X, y in data_iter:
        output = net(X)
        l = loss(output, y.view(-1, 1))
        optimizer.zero_grad() # reset gradient, equal to net.zero_grad()
        l.backward()
        optimizer.step()
    print('epoch %d, loss: %f' % (epoch, l.item()))

两种实现方式的比较

从零开始的实现(推荐用来学习)

1.能够更好的理解模型和神经网络底层的原理

2.使用pytorch的简洁实现

能够更加快速地完成模型的设计与实现

您可能感兴趣的与本文相关的镜像

PyTorch 2.5

PyTorch 2.5

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

<think> 我们遇到了一个错误:ValueError: could not convert string to float: 'FREE2DAY' 这个错误通常发生在尝试将非数值字符串(如'FREE2DAY')转换为浮点数时,而逻辑回归模型要求输入特征必须是数值型。 因此,问题在于我们的数据集中包含非数值特征(如字符串类型的分类变量),而模型无法直接处理。 解决方案:需要将分类变量(字符串类型)转换为数值型。常见方法包括: 1. 标签编码(Label Encoding):将每个类别映射为一个整数 2. 独热编码(One-Hot Encoding):为每个类别创建一个新的二进制特征 步骤: 1. 检查数据集中哪些列包含非数值数据(字符串或分类数据) 2. 根据特征的性质选择合适的编码方式 注意:在编码之前,确保已经处理了缺失值(如果有)。 具体操作: 假设我们有一个数据框`df`,其中包含分类特征。我们可以使用`sklearn`的预处理工具。 方法1:使用LabelEncoder(适用于有序分类变量) 但是要注意,LabelEncoder会给每个类别一个整数编号,这可能会引入顺序关系(例如,类别0<1<2),如果类别是无序的,则可能不合适。 方法2:使用OneHotEncoder(更常用,适用于无序分类变量) 这将为每个类别创建一个新的二值特征(0/1)。 此外,也可以使用pandas的get_dummies函数。 示例代码: 使用OneHotEncoder(推荐): ```python from sklearn.preprocessing import OneHotEncoder import pandas as pd # 假设df是包含分类特征的数据框,我们只对分类列进行编码 # 首先,找出所有分类列(object类型的列或分类类型的列) categorical_columns = df.select_dtypes(include=['object', 'category']).columns # 初始化OneHotEncoder,可以设置sparse=False得到数组(或DataFrame) encoder = OneHotEncoder(sparse_output=False, handle_unknown='ignore') # 注意:sparse_output在较新版本中替代了sparse # 对分类列进行编码 encoded_data = encoder.fit_transform(df[categorical_columns]) # 将编码后的数据转换为DataFrame,并给列命名 encoded_df = pd.DataFrame(encoded_data, columns=encoder.get_feature_names_out(categorical_columns)) # 删除原始数据中的分类列,并将编码后的数据合并 df = df.drop(categorical_columns, axis=1) df = pd.concat([df, encoded_df], axis=1) ``` 然后,用这个处理后的数据框去训练逻辑回归模型。 注意:如果训练集和测试集分开,应确保使用同一个编码器,以避免列不一致。因此,最好使用一个转换流程(如Pipeline)来封装。 方法3:使用ColumnTransformer和Pipeline(更规范) 示例: ```python from sklearn.compose import ColumnTransformer from sklearn.pipeline import Pipeline from sklearn.linear_model import LogisticRegression # 定义分类列和数值列 categorical_columns = ... # 分类列名列表 numerical_columns = ... # 数值列名列表 preprocessor = ColumnTransformer( transformers=[ ('num', 'passthrough', numerical_columns), ('cat', OneHotEncoder(handle_unknown='ignore'), categorical_columns) ]) # 创建包含预处理和模型的流水线 model = Pipeline(steps=[ ('preprocessor', preprocessor), ('classifier', LogisticRegression()) ]) # 然后直接使用model.fit(X_train, y_train),它会自动处理分类变量 ``` 这样,在预测时,同样的预处理会自动应用到测试数据上。 错误原因总结: 原始数据中的某个特征(可能是“配送方式”或类似特征)包含了字符串值(如'FREE2DAY'),而逻辑回归模型在拟合时要求所有特征都是数值型,因此需要转换。 特别提醒:目标变量(标签)如果是字符串,通常使用LabelEncoder进行转换(因为目标变量通常是类别,而且是有序的,但逻辑回归本身支持二分类和多分类,目标变量编码为整数即可)。 如果目标变量是字符串,可以这样转换: ```python from sklearn.preprocessing import LabelEncoder le = LabelEncoder() y = le.fit_transform(y) ``` 之后,如果需要原始标签,可以使用`le.inverse_transform`。 根据以上思路,我们可以解决该错误。 相关问题: 1. 如何选择分类变量的编码方式? 2. 独热编码后为什么会出现维度爆炸?如何解决? 3. 在流水线中如何同时处理数值特征和分类特征? 4. 如何处理未知类别(在测试集中出现训练集未出现的类别)? </think> 错误 `ValueError: could not convert string to float: 'FREE2DAY'` 表示您的数据集中包含非数值型特征(如字符串"FREE2DAY"),而 scikit-learn 的逻辑回归模型要求所有输入特征必须是数值类型。以下是系统解决方案: --- ### 解决方案步骤 1. **识别非数值特征** ```python # 查看数据类型统计 print(df.dtypes.value_counts()) # 检查特定列的非数值值 print(df['含有问题的列名'].unique()) ``` 2. **分类变量编码** - **独热编码**(推荐,适用于无序分类变量) ```python from sklearn.preprocessing import OneHotEncoder encoder = OneHotEncoder(sparse_output=False) encoded_features = encoder.fit_transform(df[['categorical_column']]) ``` *输出示例:* ``` FREE2DAY -> [1, 0, 0] NEXTDAY -> [0, 1, 0] STANDARD -> [0, 0, 1] ``` - **标签编码**(仅适用于有序分类变量) ```python from sklearn.preprocessing import LabelEncoder le = LabelEncoder() df['encoded_column'] = le.fit_transform(df['categorical_column']) ``` 3. **完整处理流程** ```python import pandas as pd from sklearn.compose import ColumnTransformer from sklearn.preprocessing import OneHotEncoder, StandardScaler from sklearn.linear_model import LogisticRegression # 假设数据框结构示例 df = pd.DataFrame({ 'feature1': [1, 2, 3], 'shipping': ['FREE2DAY', 'NEXTDAY', 'STANDARD'], # 问题列 'target': [0, 1, 0] }) # 定义预处理流水线 preprocessor = ColumnTransformer( transformers=[ ('num', StandardScaler(), ['feature1']), # 数值列标准化 ('cat', OneHotEncoder(), ['shipping']) # 分类列编码 ]) # 创建模型流水线 model = Pipeline([ ('preprocessor', preprocessor), ('classifier', LogisticRegression()) ]) # 训练模型 X = df.drop('target', axis=1) y = df['target'] model.fit(X, y) ``` --- ### 关键原理说明 1. **逻辑回归的数学要求** 模型基于线性方程 $z = \beta_0 + \beta_1x_1 + \cdots + \beta_nx_n$ ,所有 $x_i$ 必须是实数 $\mathbb{R}$。字符串无法直接参与计算[^1]。 2. **编码选择依据** | 方法 | 适用场景 | 数学表达 | |------|----------|----------| | 独热编码 | 无序类别(如快递方式) | $x \in \{0,1\}^k$ | | 标签编码 | 有序类别(如学历等级) | $x \in \mathbb{Z}$ | 3. **维度膨胀问题** 当分类变量有 $k$ 个类别时,独热编码会增加 $k$ 个新特征。可通过 `OneHotEncoder(drop='first')` 删除冗余维度[^2]。 --- ### 错误预防措施 1. **数据预检** ```python # 检测非数值列 non_numeric = df.select_dtypes(exclude=['int', 'float']).columns print(f"需处理的列: {list(non_numeric)}") ``` 2. **自动化处理工具** ```python from sklearn.pipeline import make_pipeline from sklearn.compose import make_column_transformer preprocessor = make_column_transformer( (OneHotEncoder(), ['categorical_col']), remainder='passthrough' # 其他列保留 ) ``` --- ### 相关问题 1. 如何处理高基数分类变量(超过100个类别)? 2. 类别不平衡时编码会导致什么问题?如何缓解? 3. 如何避免测试集中出现训练时未见的类别? 4. 在深度学习中处理分类变量有哪些替代方法? [^1]: 逻辑回归要求输入特征为实数空间 [^2]: 独热编码通过虚拟变量表示分类特征
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值