本文用仿真工具录制下训练数据后,存到本地CSV文件中,本文仅用方向盘转角速度进行训练。
代码示例采用Jupyter编码,如在其他编辑器运行问题,请使用Jupyter.
CSV文件中存储的数据如下:
问题定义:
数据收集:
数据预处理:
特征选择与工程:
模型选择:
模型训练:
模型评估:
模型优化:
模型验证与部署:
后期维护与更新:
代码示例:
- import numpy as np
- import pandas as pd
-
- # 读取数据
- data = pd.read_csv('driving_log.csv',names=["center","left","right","steering","throtle","reverse","speed"])
-
- # 数据展示
- # 把方向盘的转角数据分成20份
- counts,angle = np.histogram(data["steering"],20)
- # angle有21个数据,把两次数据加在一起再求个平均,就只有20份数据了,这是中心点坐标
- center = (angle[1:]+angle[:-1])/2
- import matplotlib.pyplot as plt
- # 柱状图展示自动驾驶数据分布情况:可以看出来角度为0直行的数据过多,需要进行剪裁
- # plt.bar(center,counts,width=0.1)
-
-
- # 数据清洗:根据数据的分布规律,把结果一直一些特别多的训练数据给删除掉
- # 过滤出直行的数据
- # 记录所有的直行数据的位置信息
- li = []
- for i in range(0,len(data["steering"])):
- if data["steering"][i] == 0:
- li.append(i)
-
- # print(len(li))
- # 画线,从中间切一刀
- # plt.plot((-1,1),(300,300))
- # plt.bar(center,counts,width=0.1)
- # 洗牌操作,只保留300 条记录
- np.random.shuffle(li)
- rm_list = li[300:]
- # np.drop 丢弃某些数据, 参数inplace=True: 在原来的数据集上操作
- data.drop(rm_list, inplace=True)
-
-
-
- counts,angle = np.histogram(data["steering"],20)
- # angle有21个数据,把两次数据加在一起再求个平均,就只有20份数据了,这是中心点坐标
- center = (angle[1:]+angle[:-1])/2
- plt.bar(center,counts,width=0.1)
- info = data[["center","steering"]]
- info.to_csv("info.csv")
- from sklearn.model_selection import train_test_split
- import cv2
- # 返回值:训练集、测试集、训练集结果、测试集结果
- X_train,X_test,Y_train,Y_test = train_test_split(info["center"],info["steering"],test_size=0.2,random_state=23)
- print(len(X_train),len(Y_test))
- # 绘制转角数据直方图,用于查看数据集分布情况
- plt.subplot(121)# 121:1行两列第一个
- plt.hist(Y_train,20,width=0.1)
- plt.subplot(122)# 121:1行两列第2个
- plt.hist(Y_test,20,width=0.1)
-
- # 显示训练集中索引为2的图片
- img = plt.imread(X_train.iloc[2])
- # plt.imshow(img)
-
- # 图片预处理:图片
- # 去掉引擎盖部分和天空部分,仅保留训练相关部分
- img = img[60:135,:,:]
- # 高斯模糊去除燥点
- img = cv2.GaussianBlur(img,(3,3))
- # 按照论文中,放入的图片尺寸为宽200*高66,YUV格式,YUV 格式很适合做图形分割与分类
- img = cv2.resize(img,(200,66))
- img = cv2.cvtColor(img,cv2.COLOR_RGB2YUV)
-
-
- # 图片预处理函数:宽高、颜色模式调整
- def data_preprocessing(img_path):
- img = cv2.imread(img_path)
- img = img[60:135,:,:]
- img = cv2.GaussianBlur(img,(3,3),1)
- img = cv2.resize(img,(200,66))
- img = cv2.cvtColor(img,cv2.COLOR_RGB2YUV)
- return img
-
- # 绘图后对比原图和预处理后的图
- random_index = np.random.randint(len(X_train))
- src_img = plt.imread(X_train.iloc[random_index])
- dst_img = data_preprocessing(X_train.iloc[random_indexa])
- #
- plt.subplot(121)
- plt.imshow(src_img)
- plt.subplot(122)
- plt.imshow(dst_img)
-
- plt.show()
-
-
- # 准备卷积神经网络CNN需要的训练数据
- train_img = []
- for i in range(0,len(X_train)):
- img = data_preprocessing(X_train.iloc[i])
- # 归一化操作
- img = img/255.0
- train_img.append(img)
-
- X_train_imgs = np.array(train_img)
-
-
- # 准备卷积神经网络CNN需要的训练数据
- test_img = []
- for i in range(0,len(X_test)):
- img = data_preprocessing(X_test.iloc[i])
- # 归一化操作
- img = img/255.0
- test_img.append(img)
-
- X_test_imgs = np.array(test_img)
-
- y_train = y_train.to_numpy()
- y_test = y_test.to_numpy()
- # 模型输出结果是-1 到 1的角度值,所以是回归问题。
- # 回归问题用误差函数MSE,分类问题用的Sigmoid
-
- import tensorflow as tf
- # 线性模型库
- model = tf.keras.Sequential()
- # 卷积层
- model.add(tf.keras.layers.Conv2D((24,(5,5),strides=(2,2),input_shape=(66,200),activation="relu"))
-
- model.add(tf.keras.layers.Conv2D((36,(5,5),strides=(2,2),activation="relu"))
-
- model.add(tf.keras.layers.Conv2D((48,(5,5),strides=(2,2),activation="relu"))
-
-
- model.add(tf.keras.layers.Conv2D((64,(3,3),activation="relu"))
- model.add(tf.keras.layers.Conv2D((64,(3,3),activation="relu"))
- model.add(tf.keras.layers.Flatten())
-
- model.add(tf.keras.layers.Dense(1164,activation="relu"))
- model.add(tf.keras.layers.Dense(100,activation="relu"))
- model.add(tf.keras.layers.Dense(50,activation="relu"))
- model.add(tf.keras.layers.Dense(10,activation="relu"))
- model.add(tf.keras.layers.Dense(1)) # 方向盘回归问题
- model.summary()
-
-
- # 模型编译
- model.compile(optimizer="rmsprop",loss="mse")
- # 模型训练
- history = model.fit(X_train_img,y_train,epochs=50,batch_size=128,validation_data=(X_test_img,y_test))
-
- # 展示训练集与验证集的损失比较
- plt.plot(history.history["loss"])
- plt.plot(history.history["val_loss"])
- plt.legend(["train","test"])
- plt.show()
- # 发现测试集表现不稳定,经常有尖锐的差距,说明模型训练过程中泛化能力差,可以增加随机取消神经元工作
- # relu用于在数据集不大但模型参数很多,可能出现梯度消失模型不收敛的情况,需要换其他的激活函数
-
- # 线性模型库
- model = tf.keras.Sequential()
- # 卷积层
- model.add(tf.keras.layers.Conv2D((24,(5,5),strides=(2,2),input_shape=(66,200),activation="elu"))
-
- # Dropout增加泛化能力,去除尖锐部分,减少训练过程中抖动情况
- model.add(tf.keras.layers.Dropout(0.5))
-
- model.add(tf.keras.layers.Conv2D((36,(5,5),strides=(2,2),activation="elu"))
- model.add(tf.keras.layers.Conv2D((48,(5,5),strides=(2,2),activation="elu"))
-
- # Dropout增加泛化能力,去除尖锐部分,减少训练过程中抖动情况
- model.add(tf.keras.layers.Dropout(0.2))
-
- model.add(tf.keras.layers.Conv2D((64,(3,3),activation="elu"))
- model.add(tf.keras.layers.Conv2D((64,(3,3),activation="elu"))
- model.add(tf.keras.layers.Flatten())
-
- model.add(tf.keras.layers.Dense(1164,activation="elu"))
- model.add(tf.keras.layers.Dense(100,activation="elu"))
- model.add(tf.keras.layers.Dense(50,activation="elu"))
-
- # Dropout增加泛化能力,去除尖锐部分,减少训练过程中抖动情况
- model.add(tf.keras.layers.Dropout(0.2))
-
- model.add(tf.keras.layers.Dense(10,activation="elu"))
- model.add(tf.keras.layers.Dense(1)) # 方向盘回归问题
- model.summary()
-
-
- # 模型编译
- model.compile(optimizer="rmsprop",loss="mse")
- # 模型训练
- history = model.fit(X_train_img,y_train,epochs=50,batch_size=128,validation_data=(X_test_img,y_test))
- model.save("model.h5")
- # 展示训练集与验证集的损失比较
- plt.plot(history.history["loss"])
- plt.plot(history.history["val_loss"])
- plt.legend(["train","test"])
- plt.show()
- test_index = np.random.randint(len(X_test_imgs))
- models = tf.keras.models.load_model("model.h5")
- result = models.predict(X_test_imgs[test_index].reshape(1,66,200,3))
- predict_result = result[0][0]