赞
踩
为了使得自己的知识成为体系,首先明确一点,知识不需要去记忆,有个印象即可,不记得就去百度,重要的是锻炼思维以及编程能力(拿到问题如何解决问题的能力。)
那么,从这个时间点开始,便开始首先搞定基础,python基础已经有了,接着就是深度学习框架pytorch的基础,所有的基础我们都不去纠结它里面有什么东西,找一个问题,去解决,干就完事了,重要的是训练解决问题的能力。好了,废话,就这么多了。
一般来说,个人认为,深度学习的”hello world“就是泰坦尼克号了,于是我们入门的第一个项目就是泰坦尼克船上的生存乘客的预测了。
一般来说,深度学习的步骤主要有以下四步:
1.准备和分析数据;
2.搭建网络模型;
3.训练模型;
4.使用和评估模型。
搭建一个多层的全连接神经网络,通过对泰坦尼克号船上的乘客的数据进行拟合,预测乘客是否能够在灾难中存活下来。
首先,我们观察一下数据每一列的特征分别所代表的含义
该数据表总共有1309行,14列,其中每一类表示的特征意义如下:
pclass:乘客舱位的等级(1,2,3)
survived:是否获救(1,获救,0,没有获救)
name:姓名
sex:性别
age:年龄
sibsp:兄弟姐妹/配偶
parch:父母/孩子
ticket:票价
cabin:船舱号码
embarked:登船港口
boat:救生艇
body:身份证号
home.dest:家庭住址
样本的数据特征主要可以分为两类:离散数据特征和连续数据特征。
1.离散数据特征
类似于分类任务中的标签数据,数据之间没有连续性。
在对离散数据做特征变换的时候,主要分为两类处理:
(1)具有固定类别:直接按照类别数目进行变换;
(2)没有固定类别:比如姓名这种,可以使用hash算法,或者词向量技术,one-hot编码等。
2.连续数据特征
类似于回归任务中的标签数据,例如年龄,时间等,在做特征变换时,经常做对数运算或者归一化处理,使其具有统一的值域。
3.离散数据与连续数据之间相互转化
在实际应用中,需要根据数据的特性选择合适的转化方式,有时还需要实现连续数据与离散数据之间的相互转化。
例如,在对一个值域跨度很大(0.1~10000)的特征属性记性数据预处理的时候,可以有以下3种方法。
(1)将其按照最大值、最小值进行归一化处理。
(2)对其使用对数运算。
(3)按照其分布情况将其分为几类,做离散化处理。
具体选择哪一种方法还要看数据的分布情况。假设数据中有90%的样本在0.1-1之间,只有10%的样本在1000-10000之间,那么使用第一种和第二种明显不合理,因为这两种方法会将90%的样本与10%的样本分开,并不能很好地体现出这90%的样本的内部分布情况。
而使用第三种方法,可以按照样本在不同区间的分布数量对样本进行分类,让样本内部的分布特征更好的表达出来。
(上面的描述我需要找到相关的案例才能理解,不然就像抄了一遍意义不大!!!)
本案例中的样本的离散数据处理比较简单,具体的操作如下:
(1)将离散数据转换为one-hot编码;
(2)对数据中的nan值进行过滤填充;
(3)剔除无用的数据列。
1.剔除无用的数据列
首先根据经验和常识分析,是否获救与乘客的名字(name)、票号(ticket)、船舱号码(cabin)等信息无关,因此直接在数据中删除。操作代码如下:
- # 处理数据
- # 1.提出无用的数据
- titanic_data = titanic_data.drop(["name", 'ticket', 'cabin', 'boat', 'body', 'home.dest', 'pclass'], axis=1) # axis=1 按列删除
- print(titanic_data.columns) # 观察数据的列属性特征
2.将离散数据转换为one-hot编码
使用pandas库中的get_dummies()函数可以将离散的数据转成one-hot编码。具体代码如下。
- # 用哑变量将指定字段转成one-hot
- # 虚拟变量(dummy variable)也叫哑变量,翻译不同而已。因为dummy的含义有假的、虚拟的、哑的等各种含义,所以国内翻译也不一样。但是他们俩是一回事。
- # 这里应该是指我们使用的这个函数
- titanic_data = pd.concat([titanic_data,
- pd.get_dummies(titanic_data["sex"]),
- pd.get_dummies(titanic_data['embarked'], prefix="embarked"), # prefix:指定前缀
- pd.get_dummies(titanic_data["pclass"], prefix="class")], axis=1)
- # titanic_data.drop(["sex",'embarked','pclass'],axis=1) # 同时删除掉原来的列表属性
- print(titanic_data.columns) # 查看当前的列属性
- print(titanic_data["sex"]) # 查看当前的sex值
- print(titanic_data['female']) # 查看female对应的值
- titanic_data = titanic_data.drop(["sex",'embarked','pclass'],axis=1) # 同时删除掉原来的列表属性
输出为:
- Index(['pclass', 'survived', 'sex', 'age', 'sibsp', 'parch', 'fare',
- 'embarked', 'female', 'male', 'embarked_C', 'embarked_Q', 'embarked_S',
- 'class_1', 'class_2', 'class_3'],
- dtype='object')
- 0 female
- 1 male
- 2 female
- 3 male
- 4 female
- ...
- 1304 female
- 1305 female
- 1306 male
- 1307 male
- 1308 male
- Name: sex, Length: 1309, dtype: object
- 0 1
- 1 0
- 2 1
- 3 0
- 4 1
- ..
- 1304 1
- 1305 1
- 1306 0
- 1307 0
- 1308 0
- Name: female, Length: 1309, dtype: uint8
在输出的结果中,female列之后都是one-hot转码后生成的新列,其中female为sex列中的离散值。也就是说在sex中,列中为female的值,在female中值为1,其他为0。同样在male中的,sex为male的,值也为1,其他为0.
3.对nan值进行过滤填充
样本中并不是每一个属性都有数据的。没有数据的部分再pandas中会被解析成nan值。因为模型无法对无效值nan进行处理,所以需要对nan值进行过滤并填充。
在本例中,只对两个连续属性的数据列进行nan值处理,即age和fare属性。具体代码如下:
- # 3.对nan值进行处理
- titanic_data["age"] = titanic_data["age"].fillna(titanic_data["age"].mean())
- titanic_data["fare"] = titanic_data["fare"].fillna(titanic_data["fare"].mean())
在以上代码中,调用了fillna()函数对nan值进行了过滤,并用该数据列中的平均值进行填充。
将survived列从数据列表中单独提取出来作为标签。将数据列中剩下的数据作为输入样本。
将样本和标签按照30%和70%比例分成测试集和训练数据集。具体代码如下:
- # 4.分离数据样本
- labels = titanic_data["survived"].to_numpy()
- print(labels)
-
- titanic_data = titanic_data.drop(["survived"],axis=1)
- data = titanic_data.to_numpy()
- print(data)
-
- # 样本的属性名称
- feature_names = list(titanic_data.columns)
- print(feature_names)
-
- # 将样本分为训练和测试两部分
- np.random.seed(10) # 设置随机种子,保证每次运行随机出来的样本一致,从而方便他人复现实验
- train_indices = np.random.choice(len(labels),int(0.7*len(labels)),replace=False)
- #numpy.random.choice(a, size=None, replace=True, p=None)
- #从a(只要是ndarray都可以,但必须是一维的)中随机抽取数字,并组成指定大小(size)的数组
- #replace:True表示可以取相同数字,False表示不可以取相同数字
- #数组p:与数组a相对应,表示取数组a中每个元素的概率,默认为选取每个元素的概率相同。
- print(len(train_indices))
-
- test_indices = list(set(range(len(labels)))-set(train_indices))
- print(len(test_indices))
-
- train_features = data[train_indices]
- train_labels = labels[train_indices]
- test_features = data[test_indices]
- test_labels = labels[test_indices]
- print(test_features)
紧接着,我们搭建一个3层全连接网络的模型,每个网络层使用Mish作为激活函数。该网络模型使用交叉熵的损失的计算方法。具体代码如下:
- # 5.搭建网络模型
-
- # 定义Mish激活函数
- class Mish(nn.Module):
- def __init__(self):
- super().__init__()
- def forward(self,x):
- x = x * (torch.tanh(F.softplus(x)))
- return x
-
- torch.manual_seed(0) # 设置随机种子,是的每次初始化的权重相同,方便他人复现结果
-
- class ThreelinearModel(nn.Module):
- def __init__(self):
- super(ThreelinearModel, self).__init__()
- self.linear1 = nn.Linear(12,12)
- self.mish1 = Mish()
- self.linear2 = nn.Linear(12,8)
- self.mish2 = Mish()
- self.linear3 = nn.Linear(8,2)
- self.softmax = nn.Softmax(dim=1)
- self.criterion = nn.CrossEntropyLoss()
-
- def forward(self,x):
- x = self.linear1(x)
- x = self.mish1(x)
- x = self.linear2(x)
- x = self.mish2(x)
- x = self.linear3(x)
- return self.softmax(x)
-
- def getloss(self,x,y):
- y_pre = self.forward(x)
- loss = self.criterion(y_pre,y)
- return loss
这里我们实现一个简单的训练过程,代码如下:
- # 6.训练模型
- if __name__ == '__main__':
- net = ThreelinearModel() # 实例化模型对象
- num_epochs = 1000 # 设置训练次数
- optimizer = torch.optim.Adam(net.parameters(), lr=0.001) # 定义优化器
-
- # 将输入的样本标签转化为张量
- input_tensor = torch.from_numpy(train_features).type(torch.FloatTensor)
- label_tensor = torch.from_numpy(train_labels)
- losses = [] # 定义列表,用于接收每一步的损失值
- for epoch in range(num_epochs):
- loss = net.getloss(input_tensor, label_tensor)
- losses.append(loss.item())
- optimizer.zero_grad() # 清空之前的梯度值
- loss.backward() # 反向传播损失值
- optimizer.step() # 更新参数
- if epoch % 20 == 0:
- print("epoch{}/{} => Loss:{:.2f}".format(epoch+1, num_epochs,loss.item()))
- os.makedirs("models", exist_ok=True) # 创建文件夹
- torch.save(net.state_dict(), "models/titanic_model.pt") # 保存模型
-
- # 显示loss值的可视化结果
- plot_losses(losses)
-
- # 输出训练的性能结果
- out_probs = net(input_tensor).detach().numpy()
- out_classes = np.argmax(out_probs, axis=1)
- print("train Accuracy:", sum(out_classes == train_labels) / len(train_labels))
显示训练的可视化结果如下:
- epoch8841/9000 => Loss:0.45
- epoch8861/9000 => Loss:0.45
- epoch8881/9000 => Loss:0.45
- epoch8901/9000 => Loss:0.45
- epoch8921/9000 => Loss:0.45
- epoch8941/9000 => Loss:0.45
- epoch8961/9000 => Loss:0.45
- epoch8981/9000 => Loss:0.45
-
- train Accuracy: 0.8526200873362445
训练好模型之后我们开始对,模型进行测试评估。代码如下:
- # 测试模型
- test_input_tensor = torch.from_numpy(test_features).type(torch.FloatTensor)
- out_probs = net(test_input_tensor).detach().numpy()
- out_classes = np.argmax(out_probs, axis=1)
- print("test Accuracy:" ,sum(out_classes == test_labels)/ len(test_labels))
得到的结果如下:
test Accuracy: 0.7964376590330788
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。