本科做的科研项目是基于深度学习的自动调制识别技术,在接触深度学习时,在数据处理、性能分析方面遇到了许多问题,此博客用于记录。
数据处理
import torch # 导入PyTorch库
import pickle # 导入pickle库
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import torch.optim as optim
import numpy as np
import torch.optim as optim
import torch.nn as nn
import torch.nn.functional as F
import torch, gc
import random
import matplotlib.pyplot as plt
from torch.utils.data import DataLoader, TensorDataset
from torchvision.utils import save_image
from torch.utils.data import Dataset
gc.collect()
torch.cuda.empty_cache()
def to_onehot(yy):
yy1 = np.zeros([len(yy), max(yy) + 1])
yy1[np.arange(len(yy)), yy] = 1
return yy1
def setup_seed(seed):
torch.manual_seed(seed)
torch.cuda.manual_seed_all(seed)
np.random.seed(seed)
random.seed(seed)
torch.backends.cudnn.deterministic = True
# 设置随机数种子
setup_seed(624)
def to_onehot(yy):
yy1 = np.zeros([len(yy), max(yy) + 1])
yy1[np.arange(len(yy)), yy] = 1
return yy1
Xd = pickle.load(open("RML2016.10a_dict.pkl", 'rb'), encoding='latin')
snrs, mods = map(lambda j: sorted(list(set(map(lambda x: x[j], Xd.keys())))), [1, 0])
X = []
lbl = []
for mod in mods:
for snr in snrs:
X.append(Xd[(mod, snr)])
for i in range(Xd[(mod, snr)].shape[0]):
lbl.append((mod, snr))
X = np.vstack(X)
# 计算各部分数据集的比例
train_ratio = 0.4
val_ratio = 0.3
test_ratio = 0.3
# 设置随机种子
n_examples = X.shape[0]
n_train = int(n_examples * train_ratio)
n_val = int(n_examples * val_ratio)
n_test = n_examples - n_train - n_val
# 创建随机索引
idx = np.random.permutation(n_examples)
train_idx = idx[:n_train]
val_idx = idx[n_train:n_train+n_val]
test_idx = idx[n_train+n_val:]
# 划分数据集
X_train = X[train_idx]
X_val = X[val_idx]
X_test = X[test_idx]
print(X_train.shape)
trainy = list(map(lambda x: mods.index(lbl[x][0]), train_idx))
Y_train = torch.tensor(trainy)
valy = list(map(lambda x: mods.index(lbl[x][0]), val_idx))
valy = torch.tensor(valy)
testy = list(map(lambda x: mods.index(lbl[x][0]), test_idx))
testy = torch.tensor(testy)
X_train = torch.tensor(X[train_idx]).view(len(train_idx),1,2,128)
X_val = torch.tensor(X[val_idx]).view(len(val_idx),1,2,128)
X_test = torch.tensor(X[test_idx]).view(len(test_idx),1,2,128)
# 将维度从[𝑁,2,128]调整为[𝑁,1,2,128]
print(X_train.shape,Y_train.shape)
#数据归一化
print(testy[1024])
将IQ数据归一化
切记,数据喂入网络前一定要进行归一化,刚开始学习时发现未对数据归一化,数据的分布差异较大,因此网络无法学习。
# 获得A/P特征
def toAP(iqdata):
data = iqdata
data = torch.sqrt(iqdata[:,:,0,:]**2+iqdata[:,:,1,:]**2)
phase = torch.atan(iqdata[:,:,1,:]/iqdata[:,:,0,:])
#增加一个维度
data = data.unsqueeze(2)
phase = phase.unsqueeze(2)
#print(data.shape,phase.shape)
return data,phase
train_data,train_phase = toAP(X_train)
val_data,val_phase = toAP(X_val)
test_data,test_phase=toAP(X_test)
#得到(batch_size,1,2,128维度)
train_ap = torch.cat((train_data,train_phase),2)
val_ap = torch.cat((val_data,val_phase),2)
test_ap = torch.cat((test_data,test_phase),2)
train_data = train_data.view(train_data.size(0),-1)
train_phase = train_phase .view(train_phase .size(0),-1)
val_data = val_data.view(val_data.size(0),-1)
val_phase = val_phase .view(val_phase .size(0),-1)
test_data = test_data.view(test_data.size(0),-1)
test_phase = test_phase .view(test_phase .size(0),-1)
X_train = F.normalize(X_train,p=2, dim=3)
X_val = F.normalize(X_val,p=2, dim=3)
X_test = F.normalize(X_test,p=2,dim=3)
train_ap = F.normalize(train_ap,p=2, dim=3)
val_ap = F.normalize(val_ap,p=2, dim=3)
test_ap = F.normalize(test_ap,p=2,dim=3)
print(train_ap[0:1,...],train_data.shape)
自定义数据集
class CustomDataset(Dataset):
def __init__(self,iq_data, labels):
self.iq_data = iq_data
self.labels = labels
def __len__(self):
return len(self.labels)
def __getitem__(self, index):
iq = self.iq_data[index]
label = self.labels[index]
return iq, label
num_classes=11
batch_size = 200
train_tuples = list(zip( [X_train1.clone().detach() for X_train1 in X_train], [Y_train_tensor.clone().detach() for Y_train_tensor in Y_train]))
val_tuples = list(zip( [X_val1.clone().detach() for X_val1 in X_val], [valy_tensor.clone().detach() for valy_tensor in valy]))
test_tuples = list(zip( [X_test1.clone().detach() for X_test1 in X_test], [testy_tensor.clone().detach() for testy_tensor in Y_test]))
#train_tuples = list(zip([P_tensor.clone().detach() for P_tensor in P_tensor_list], [X_train1.clone().detach() for X_train1 in X_train], [Y_train_tensor.clone().detach() for Y_train_tensor in Y_train]))
#val_tuples = list(zip([Pv_tensor.clone().detach() for Pv_tensor in Pv_tensor_list], [X_val1.clone().detach() for X_val1 in X_val], [valy_tensor.clone().detach() for valy_tensor in valy]))
#test_tuples = list(zip([Pt_tensor.clone().detach() for Pt_tensor in Pt_tensor_list], [X_test1.clone().detach() for X_test1 in X_test], [testy_tensor.clone().detach() for testy_tensor in testy]))
train_dataset = CustomDataset(*[torch.stack(tensors) for tensors in zip(*train_tuples)])
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size, drop_last=True)
val_dataset = CustomDataset(*[torch.stack(tensors) for tensors in zip(*val_tuples)])
val_loader = DataLoader(val_dataset, shuffle=False, batch_size=batch_size, drop_last=True)
test_dataset = CustomDataset(*[torch.stack(tensors) for tensors in zip(*test_tuples)])
test_loader = DataLoader(test_dataset, shuffle=False, batch_size=batch_size, drop_last=True)
网络搭建(这里仅简单的搭了一个基础网络)
# 定义CLDNN模型
class CLDNN(nn.Module):
def __init__(self):
super(CLDNN, self).__init__()
self.conv1 = nn.Sequential(
nn.BatchNorm2d(1),
nn.Conv2d(in_channels= 1, out_channels = 256, kernel_size= (1, 3)),
nn.ReLU(),
# nn.MaxPool2d(kernel_size= (1,2))
nn.Dropout(0.5)
)
self.conv2 = nn.Sequential(
nn.BatchNorm2d(256),
nn.Conv2d(in_channels= 256, out_channels= 80, kernel_size=(2,3)),
nn.ReLU(),
# nn.MaxPool2d(kernel_size= (1,2))
nn.Dropout(0.5)
)
self.conv3 = nn.Sequential(
nn.BatchNorm2d(80),
nn.Conv2d(in_channels= 80, out_channels= 80, kernel_size=(1,3)),
nn.ReLU(),
# nn.MaxPool2d(kernel_size= (1,2))
nn.Dropout(0.5)
)
self.conv4 = nn.Sequential(
nn.BatchNorm2d(80),
nn.Conv2d(in_channels= 80, out_channels= 80, kernel_size=(1,3)),
nn.ReLU(),
# nn.MaxPool2d(kernel_size= (1,2))
nn.Dropout(0.5)
)
self.lstm1 = nn.Sequential(
nn.LSTM(input_size= 80, hidden_size= 50, num_layers= 1, batch_first= True)
)
self.fc1 = nn.Sequential(
nn.Linear(in_features= 50, out_features= 128),
nn.ReLU(),
nn.Dropout(0.5),
)
self.fc2 = nn.Sequential(
nn.Linear(in_features= 128, out_features= 11)
)
def initialize_weight(self):
for m in self.modules():
if isinstance(m, nn.Conv2d):
nn.init.xavier_uniform_(m.weight)
elif isinstance(m, nn.BatchNorm2d) or isinstance(m, nn.BatchNorm1d):
nn.init.constant_(m.weight, 1)
nn.init.constant_(m.bias, 0)
elif isinstance(m, nn.Linear):
nn.init.kaiming_normal_(m.weight)
self.initialize_weight()
self.to(self.hyper.device)
def forward(self, x):
#x = torch.unsqueeze(x, 1)
# x = x.view(x.shape[0],1, 2, 128)
x = self.conv1(x)
x = self.conv2(x)
x = self.conv3(x)
x = self.conv4(x)
x = torch.transpose(x[:,:,0,:],1,2)
x, (h,c) = self.lstm1(x)
x = x[:,-1,:]
x = self.fc1(x)
out = self.fc2(x)
return out
model = CLDNN()
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") #把模型迁移导到GPU
model.to(device) #模型、参数迁移到GPU
# construct loss and optimize
criterion = torch.nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
optimizer.zero_grad() #梯度清0
np_epoch =120
print("Total number of paramerters in networks is {} ".format(sum(x.numel() for x in model1.parameters())))
网络训练
for param in model.parameters():
if len(param.shape)>=2:
torch.nn.init.xavier_normal_(param)
def train(epoch):
ttotal=0
right = 0
model.train()
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0):
iq,target = data
iq,target = iq.to(device),target.to(device) #输入和输出都迁移到GPU,且需要放在同一块显卡
running_loss = 0.0
num_mods = 0
optimizer.zero_grad() #梯度清0
outputs= model(iq)
loss = criterion(outputs, target)
loss.backward()#计算梯度
optimizer.step()#优化
_,predicted = torch.max(outputs.data, dim=1)
ttotal += target.size(0)
right =right+ predicted.eq(target.data.view_as(predicted)).sum()
running_loss += loss.item()#计算损失总和
ac = right/ttotal
return running_loss,ac
# 观察训练后的模型在捏造数据上的表现
def val(G):
correct = 0
total = 0
acc_val=0
model.eval()
with torch.no_grad():
for data in val_loader:
iq, target = data
iq, target = iq.to(device),target.to(device) #输入和输出都迁移到GPU,且需要放在同一块显卡
outputs = model(iq)
val_loss = criterion(outputs, target)
_, predicted = torch.max(outputs.data, dim=1)
total += target.size(0)
correct += (predicted == target).sum().item()
acc_val = correct / total
return acc_val,val_loss
#返回正确率及生成矩阵G
if __name__ == '__main__': #检测脚本是否被作为主程序直接运行
epoch_list = []
acc_list = []
loss_list = []
trainacc_list=[]
max_acc=0
min_loss=100
for epoch in range(np_epoch):
loss,train_acc=train(epoch)
acc,val_loss= val()
loss_list.append(loss)
trainacc_list.append(train_acc)
epoch_list.append(epoch)
acc_list.append(acc)
#scheduler.step()
if acc>=max_acc:
max_acc=acc
t=0
# 保存整个模型,包含模型结构和参数
torch.save(model, 'CLDNN.pt')
t = t+1
loss_list.append(loss)
trainacc_list.append(train_acc)
epoch_list.append(epoch)
acc_list.append(acc)
print(epoch,loss,train_acc,val_loss,acc)
if t>=10:
print(max_acc)
break
绘制混淆矩阵函数
import itertools
def confusion_matrix(preds,labels,conf_matrix):
for p,t in zip(preds,labels):
conf_matrix[p,t]+=1
return conf_matrix
# 绘制混淆矩阵
def plot_confusion_matrix(cm, classes, normalize=True, title='Confusion matrix', cmap=plt.cm.Blues):
'''
This function prints and plots the confusion matrix.
Normalization can be applied by setting `normalize=True`.
Input
- cm : 计算出的混淆矩阵的值
- classes : 混淆矩阵中每一行每一列对应的列
- normalize : True:显示百分比, False:显示个数
'''
if normalize:
cm = cm.astype('float') / cm.sum(axis=0)[:, np.newaxis]
print("Normalized confusion matrix")
else:
print('Confusion matrix, without normalization')
#print(cm)
plt.imshow(cm, interpolation='nearest', cmap=cmap)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(len(classes))
plt.xticks(tick_marks, classes, rotation=90)
plt.yticks(tick_marks, classes)
ax = plt.gca() # 获得当前axis
left, right = plt.xlim() # 获得x轴最大最小值
ax.spines['left'].set_position(('data', left))
ax.spines['right'].set_position(('data', right))
for edge_i in ['top', 'bottom', 'right', 'left']:
ax.spines[edge_i].set_edgecolor("white")
plt.axis("equal")
ax = plt.gca() # 获得当前axis
left, right = plt.xlim() # 获得x轴最大最小值
ax.spines['left'].set_position(('data', left))
ax.spines['right'].set_position(('data', right))
for edge_i in ['top', 'bottom', 'right', 'left']:
ax.spines[edge_i].set_edgecolor("white")
thresh = cm.max() / 2.
for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
num = '{:.2f}'.format(cm[i, j]) if normalize else int(cm[i, j])
plt.text(i, j, num,
verticalalignment='center',
horizontalalignment="center",
color="white" if float(num) > thresh else "black")
plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted label')
plt.show()
#该函数用于得到每种调制方式的准确值,先返回正确值,再返回类型总值,该代码可以优化,比如每一轮train只需要算一次总的num_mods
def accofmods(predict,y):
#记录验证集中各调制类型的个数
confusion_matrix = torch.zeros((11,11))
num_mods = torch.zeros((1,11))
#记录各调制类型正确的个数
num_mods_right = torch.zeros((1,11))
for idx in range(len(y)):
num_mods[:,y[idx]] = num_mods[:,y[idx]]+1
if predict[idx].item() == y[idx].item():
num_mods_right[:,y[idx]]=num_mods_right[:,y[idx]]+1
confusion_matrix[y[idx],y[idx]] += 1
else:
confusion_matrix[y[idx],predict[idx]]+=1
return num_mods_right,num_mods