在本篇文章中,我们将使用Kaggle提供的纽约市出租车费数据,构建一个基于深度学习的分类模型,预测出租车费是否超过10美元。我们将结合数据探索、特征工程、深度学习模型构建与优化,并对模型进行测试和评估。

1. 数据加载与预处理

        我们从数据集中加载纽约市出租车费数据,数据包括了乘客的上车和下车的经纬度、乘车时间、乘客人数等字段。我们需要基于这些输入来预测出租车费用的分类:低于10美元或等于/高于10美元。

import pandas as pd

# 加载数据集
df = pd.read_csv('../data/NYCTaxiFares.csv')
df.head()

数据概览

数据集共有120,000条记录,包含了以下字段:

  • pickup_datetime:上车时间
  • pickup_latitude:上车地点纬度
  • pickup_longitude:上车地点经度
  • dropoff_latitude:下车地点纬度
  • dropoff_longitude:下车地点经度
  • passenger_count:乘客人数
  • fare_class:目标变量,表示费用类别,0表示低于10美元,1表示等于或高于10美元
# 查看分类的分布
df['fare_class'].value_counts()

数据分类说明

  • Class 0: 费用低于10美元
  • Class 1: 费用等于或高于10美元

        约有2/3的数据属于Class 0,1/3的数据属于Class 1。这个数据不算严重的类别不平衡,但我们在建模时仍需注意平衡性。

2. 特征工程

        在开始建模之前,我们需要对原始数据进行一些处理,提取出有用的特征。例如,我们可以通过上车和下车的经纬度来计算出行距离,同时提取时间相关的特征(例如小时、星期几等)。

计算两点之间的距离

        我们可以使用haversine公式来计算两组经纬度之间的距离,该公式适用于球面距离计算,地球可以近似看作一个球体。

import numpy as np

def haversine_distance(df, lat1, long1, lat2, long2):
    """
    计算两组经纬度之间的球面距离
    """
    r = 6371  # 地球平均半径,单位为千米
    phi1 = np.radians(df[lat1])
    phi2 = np.radians(df[lat2])
    delta_phi = np.radians(df[lat2] - df[lat1])
    delta_lambda = np.radians(df[long2] - df[long1])
    a = np.sin(delta_phi / 2)**2 + np.cos(phi1) * np.cos(phi2) * np.sin(delta_lambda / 2)**2
    c = 2 * np.arctan2(np.sqrt(a), np.sqrt(1 - a))
    return r * c  # 返回距离,单位为千米

# 应用公式,生成距离特征
df['dist_km'] = haversine_distance(df, 'pickup_latitude', 'pickup_longitude', 'dropoff_latitude', 'dropoff_longitude')

提取时间特征

        通过将字符串形式的pickup_datetime转换为时间对象,我们可以提取出时间相关的特征,如小时、上午/下午以及星期几。

df['EDTdate'] = pd.to_datetime(df['pickup_datetime'].str[:19]) - pd.Timedelta(hours=4)  # 转换为美国东部时间
df['Hour'] = df['EDTdate'].dt.hour
df['AMorPM'] = np.where(df['Hour'] < 12, 'am', 'pm')
df['Weekday'] = df['EDTdate'].dt.strftime("%a")

        提取时间信息后,我们的数据框将包含以下特征:

  • dist_km: 乘车的行驶距离
  • Hour: 上车时间的小时
  • AMorPM: 上午或下午
  • Weekday: 星期几

3. 数据预处理

        在进行建模前,我们还需要对分类特征进行编码,将字符串类别转换为数字表示。我们使用pandas中的category数据类型来转换这些列。

for col in ['Hour', 'AMorPM', 'Weekday']:
    df[col] = df[col].astype('category')

# 检查转换后的数据类型
df.dtypes

        通过这种转换方式,我们将会为每个分类特征分配一个整数编码,用以表示不同的类别。

4. 模型构建

嵌入层处理

        我们将为分类特征构建嵌入层,通过将类别变量转换为嵌入向量,模型能够更好地学习到不同类别的相似性。

import torch
import torch.nn as nn

class TaxiFareModel(nn.Module):
    def __init__(self, emb_szs, n_cont, out_sz, layers, p=0.5):
        super().__init__()
        self.embeds = nn.ModuleList([nn.Embedding(ni, nf) for ni,nf in emb_szs])
        self.emb_drop = nn.Dropout(p)
        self.bn_cont = nn.BatchNorm1d(n_cont)
        
        layers_list = []
        n_emb = sum((nf for ni, nf in emb_szs))
        n_in = n_emb + n_cont
        
        for i in layers:
            layers_list.append(nn.Linear(n_in, i))
            layers_list.append(nn.ReLU(inplace=True))
            layers_list.append(nn.BatchNorm1d(i))
            layers_list.append(nn.Dropout(p))
            n_in = i
            
        layers_list.append(nn.Linear(layers[-1], out_sz))
        self.layers = nn.Sequential(*layers_list)
    
    def forward(self, x_cat, x_cont):
        embeddings = [e(x_cat[:, i]) for i, e in enumerate(self.embeds)]
        x = torch.cat(embeddings, 1)
        x = self.emb_drop(x)
        x_cont = self.bn_cont(x_cont)
        x = torch.cat([x, x_cont], 1)
        return self.layers(x)

模型训练

        我们使用交叉熵损失函数进行模型的训练,优化器选用Adam优化器。

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

# 模型训练
epochs = 300
losses = []

for epoch in range(epochs):
    y_pred = model(cat_train, con_train)
    loss = criterion(y_pred, y_train)
    losses.append(loss.item())
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

5. 模型测试与评估

        我们使用测试集进行模型评估,并计算模型的准确率。

with torch.no_grad():
    y_val = model(cat_test, con_test)
    loss = criterion(y_val, y_test)
    
print(f'测试集上的交叉熵损失: {loss.item()}')

        我们还可以打印前50个预测值及其对应的实际值,来直观地检查模型的表现。

rows = 50
correct = 0

for i in range(rows):
    pred_label = y_val[i].argmax().item()
    true_label = y_test[i].item()
    if pred_label == true_label:
        correct += 1
        
print(f'{correct} out of {rows} = {100 * correct / rows:.2f}% correct')

6. 模型保存与加载

        为了方便后续的推理和应用,我们可以将训练好的模型保存到文件中。

torch.save(model.state_dict(), 'TaxiFareClssModel.pt')

        在需要时,我们可以重新加载模型并进行推理。

model2 = TaxiFareModel(emb_szs, conts.shape[1], 2, [200, 100], p=0.4)
model2.load_state_dict(torch.load('TaxiFareClssModel.pt'))
model2.eval()  # 设置为评估模式

结语

        通过这个案例,我们学习了如何基于分类问题构建深度学习模型,预测纽约市出租车费是否超过10美元。我们通过特征工程生成了重要的特征,并利用嵌入层对分类变量进行了处理。最终,我们构建了一个深度神经网络,并对模型进行了训练和评估。

        这个案例展示了如何将深度学习应用于实际问题中,并且提供了未来可扩展的模型保存和加载方式,便于对新数据进行推理。

如果你觉得这篇博文对你有帮助,请点赞、收藏、关注我,并且可以打赏支持我!

欢迎关注我的后续博文,我将分享更多关于人工智能、自然语言处理和计算机视觉的精彩内容。

谢谢大家的支持!

Logo

NVIDIA官方入驻,分享最新的官方资源以及活动/会议信息,精选收录AI相关技术内容,欢迎大家加入社区并参与讨论。

更多推荐