这是学习视频中留下来的一个作业,我决定根据大佬的步骤来一步一步完成整个项目,项目的下载地址如下:https://www.kaggle.com/c/titanic/data
大佬的传送门:https://zhuanlan.zhihu.com/p/338974416
首先我们打开训练集,看到的数据如下
我们可以看到这个数据集里面的特征类别有,乘客序号,是否存活,船票等级,性别,年龄,在船上的亲属数量,票的号码,票价,座舱号,和登船地
所以我们需要判定哪些数据是有效的
import re //正则表达式的库 import numpy as np import pandas as pd import matplotlib.pyplot as plt import seaborn as sns //画图用的一个库 train = pd.read_csv('train.csv') test = pd.read_csv('test.csv') print('训练集:', train.shape, '测试集', test.shape) total_data = train.append(test, sort=False, ignore_index=True) //需要添加的index不出现重复
可以看到我们的数据规模
然后我们查看我们数据的摘要信息
train.info() print("-" * 40)//方便阅读输入的分隔符 test.info()
这里可以明显看出,在训练集中,年龄、座舱号、目的地有缺失值
而在测试集中,年龄、票价、座舱号有缺失值
查看总体幸存比例
total_data['Survived'].value_counts().plot.pie(autopct='%1.2f%%') //这里的正则表达式表示小数点后保留两位 plt.show()
这里再查看性别与存活率的关系
print(total_data.groupby(['Sex'])['Survived'].agg(['count', 'mean'])) //groupby来挑选组别,agg定义输出列的名称 sns.countplot(x='Sex',hue= 'Survived', data=total_data)
显然女性在登船率低于男性的情况下存活率远高于男性,所以性别是个很重要的特征
然后我们来看目的地对于存活率是否有影响,首先看看数据的概况
print(total_data['Embarked'].value_counts())
但是这里的embarked是有缺失值的,因此用众数填充Embarked空值(从哪来人最多,那就默认不知道哪里的人就从那里来里)
然后查看不同地区登船的人与存活率关系
total_data['Embarked'].fillna( total_data.Embarked.mode().values[0], inplace=True) //TURE表示直接替换原来的值 print(total_data.groupby(['Embarked'])['Survived'].agg(['count', 'mean'])) plt.figure(figsize=(10, 5)) sns.countplot(x='Embarked', hue='Survived', data=total_data) plt.title('Embarked and Survived')
可以看出,C地登船的存活率最高、其次为Q地登船、S地登船人数最多但存活率最低
这里Cabin缺失比较多,用Unknown替代缺失值
total_data['Cabin'].fillna('U', inplace=True) total_data['Cabin'] = total_data['Cabin'].map( lambda x: re.compile('([a-zA-Z]+)').search(x).group()) ///正则表达式把船票的第一个字母取出来 print(total_data.groupby(['Cabin'])['Survived'].agg(['count', 'mean']))
不难看出BDE的存活率比较高
再看看不同票等级生存的分布与不同票等级生存的几率
print(total_data.groupby(['Pclass'])['Survived'].agg(['count', 'mean'])) plt.figure(figsize=(10, 5)) sns.countplot(x='Pclass', hue='Survived', data=total_data) plt.title('Pclass and Survived') plt.show()
票等级越高存活率就越高
再来填充空白的票价
total_data['Fare'] = total_data[['Fare']].fillna( total_data.groupby('Pclass').transform(np.mean)) //把票类别所在的所有票价求均值填充
来查看票价分布
然后合并家庭人数
total_data['Family_Size'] = total_data['Parch'] + total_data['SibSp'] + 1
继续用众数填充年龄缺失值(方法不好,但是也勉强能用)
total_data['Age'].fillna( total_data.Age.mode().values[0], inplace=True)
其实做到这里发现已经做不下去了,文章给的一些处理数据方法远远超出了我的认知(巨大的打击),重新寻求一番后,发现了这个文章,适用于我来操作
https://blog.csdn.net/Learning_AI/article/details/122460458
首先我简述一下几个点,我独立在处理这些数据时有几个问题没有解决,但是文章代码给了很好的解决方案,记录一下
下面是代码部分
class TitanicDataset(Dataset): def __init__(self, filepath): xy = pd.read_csv(filepath) # xy.shape()可以得到xy的行列数 self.len = xy.shape[0] # 选取相关的数据特征 feature = ["Pclass", "Sex", "SibSp", "Parch", "Fare"] # np.array()将数据转换成矩阵,方便进行接下来的计算 # 要先进行独热表示,然后转化成array,最后再转换成矩阵 self.x_data = torch.from_numpy(np.array(pd.get_dummies(xy[feature]))) self.y_data = torch.from_numpy(np.array(xy["Survived"])) # getitem函数,可以使用索引拿到数据 def __getitem__(self, index): return self.x_data[index], self.y_data[index] # 返回数据的条数/长度 def __len__(self): return self.len
然后
# 实例化自定义类,并传入数据地址 dataset = TitanicDataset('train.csv') # num_workers是否要进行多线程服务,num_worker=2 就是2个进程并行运行 # 采用Mini-Batch的训练方法 train_loader = DataLoader(dataset=dataset, batch_size=16, shuffle=True, num_workers=0)
然后来定义模型
class Model(torch.nn.Module): def __init__(self): super(Model, self).__init__() # 要先对选择的特征进行独热表示计算出维度,而后再选择神经网络开始的维度 self.linear1 = torch.nn.Linear(6, 3) self.linear2 = torch.nn.Linear(3, 1) self.sigmoid = torch.nn.Sigmoid() # 前馈 def forward(self, x): x = self.sigmoid(self.linear1(x)) x = self.sigmoid(self.linear2(x)) return x
由于有测试集,所以还需要写一个测试函数
def test(self, x): with torch.no_grad():##在使用pytorch时,并不是所有的操作都需要进行计算图的生成(计算过程的构建,以便梯度反向传播等操作)。而对于tensor的计算操作,默认是要进行计算图的构建的,在这种情况下,可以使用 with torch.no_grad():,强制之后的内容不进行计算图构建 x = self.sigmoid(self.linear1(x)) x = self.sigmoid(self.linear2(x)) y = [] # 根据二分法原理,划分y的值 for i in x: if i > 0.5: y.append(1) else: y.append(0) return y
然后实例化模型,定义损失函数,优化器
# 实例化模型 model = Model() # 定义损失函数 criterion = torch.nn.BCELoss(reduction='mean') # 定义优化器 optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
开始训练
if __name__ == '__main__': # 采用Mini-Batch的方法训练要采用多层嵌套循环 # 所有数据都跑100遍 for epoch in range(400): # data从train_loader中取出数据(取出的是一个元组数据):(x,y) # enumerate可以获得当前是第几次迭代,内部迭代每一次跑一个Mini-Batch for i, data in enumerate(train_loader, 0): # inputs获取到data中的x的值,labels获取到data中的y值 x, y = data x = x.float() //需要转换类型,不然会报错 y = y.float() y_pred = model(x) y_pred = y_pred.squeeze(-1) //把y降维 loss = criterion(y_pred, y) print(epoch, i, loss.item()) optimizer.zero_grad() loss.backward() optimizer.step()
最后把测试集传进去
test_data = pd.read_csv('test.csv') feature = ["Pclass", "Sex", "SibSp", "Parch", "Fare"] test = torch.from_numpy(np.array(pd.get_dummies(test_data[feature]))) y = model.test(test.float())
输出结果为csv
# 输出预测结果 output = pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': y}) output.to_csv('my_predict.csv', index=False)
随机梯度下降,学习率0.01,训练100次
小批量梯度下降,学习率0.01,训练200次
我又把代码改了改了,搞成了全梯度下降
具体代码如下
import numpy as np import pandas as pd import torch xy = pd.read_csv('train.csv') len = xy.shape[0] feature = ["Pclass", "Sex", "SibSp", "Parch", "Fare"] x_data = torch.from_numpy(np.array(pd.get_dummies(xy[feature]))) y_data = torch.from_numpy(np.array(xy["Survived"])) class Model(torch.nn.Module): def __init__(self): super(Model, self).__init__() # 要先对选择的特征进行独热表示计算出维度,而后再选择神经网络开始的维度 self.linear1 = torch.nn.Linear(6, 3) self.linear2 = torch.nn.Linear(3, 1) self.sigmoid = torch.nn.Sigmoid() # 前馈 def forward(self, x): x = self.sigmoid(self.linear1(x)) x = self.sigmoid(self.linear2(x)) return x # 测试函数 def test(self, x): with torch.no_grad(): x = self.sigmoid(self.linear1(x)) x = self.sigmoid(self.linear2(x)) y = [] # 根据二分法原理,划分y的值 for i in x: if i > 0.5: y.append(1) else: y.append(0) return y # 实例化模型 model = Model() # 定义损失函数 criterion = torch.nn.BCELoss(reduction='mean') # 定义优化器 optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 防止windows系统报错 if __name__ == '__main__': # 采用Mini-Batch的方法训练要采用多层嵌套循环 # 所有数据都跑100遍 # plt.show() for epoch in range(200000): x_data = x_data.float() y_data = y_data.float() y_pred = model(x_data) y_pred = y_pred.squeeze(-1) loss = criterion(y_pred, y_data) print(epoch, loss.item()) optimizer.zero_grad() loss.backward() optimizer.step() # 测试 test_data = pd.read_csv('test.csv') feature = ["Pclass", "Sex", "SibSp", "Parch", "Fare"] test = torch.from_numpy(np.array(pd.get_dummies(test_data[feature]))) y = model.test(test.float()) # 输出预测结果 output = pd.DataFrame({'PassengerId': test_data.PassengerId, 'Survived': y}) output.to_csv('my_predict.csv', index=False)
帅的嘛还就不谈了!
但是这里还是有个问题
因为我学习的时候,明明随机梯度下降更加精确,因为随机梯度下降可以逃离局部最优点,但是为什么这里的得分反而是全梯度下降高呢?
我想了想我终于明白了,tmd因为我全梯度训练了20w次,搁谁谁不高
我换成了400次,果然,0.63分
好了,没问题了