|
**常见数据的维度与shape
图像
有高与宽,其数字存储的shape通常为[高,宽],
这个shape在CV中叫特征图,就是特征形成的图形/shape
这个维度只是表示有多少个像素点,这些像素点形成了什么样的图形,
然而每个像素点还有三色,表示着这个像素点的特征,这个维度在CV中就叫特征,
特征组成事物的元素,比如
图像由什么组成?答,像素,那像素就是特征
句子由什么组成?答,单词,那单词就是特征
在torch构架中,特征维度的个数,也叫通道数,channel / C
多张图像输入模型的shape为,[B,C,H,W]
文本
一个又一个的句子,句子由单词组成,输入torch模型的shape通常为[B,C,L]
B是批次,C是单词的维度,L是一个句子中单词的个数
在深度学习中,特征图(Feature Map)的概念通常与卷积神经网络(CNN)相关,
它指的是卷积层、池化层等处理后的输出。
在这个上下文中,特征图既不是指特征向量的维度,
也不是简单地指某个形状的二维数组(比如[H,W]),而是指经过特定层处理后的多维数据。
以RGB三色图像为例,在PyTorch框架中,一个图像张量的形状通常是 [B, C, H, W],其中:
B 是批次大小(Batch Size),即一次处理的图像数量。
C 是通道数(Channel),对于RGB图像来说,C=3,分别对应红色、绿色和蓝色通道。
H 是图像的高度(Height)。
W 是图像的宽度(Width)。
当这样的图像数据通过卷积层或池化层等处理时,
每个通道上的数据会被独立处理(或有时跨通道组合处理,如深度可分离卷积),
并生成新的特征图。
这些新的特征图通常具有不同的通道数(由卷积核的数量决定)和
可能不同的空间维度(高度和宽度,取决于步长、填充和池化等因素)。
因此,在卷积神经网络中,特征图通常指的是经过某层处理后输出的多维数据,
其形状一般为 [B, C', H', W'],
其中 C' 是新的通道数,H' 和 W' 是新的高度和宽度。
具体到你的问题,以 [B, 3, H, W] 的图像为例,
特征图既不是指 3(这是输入图像的通道数),也不是指 [H, W](这是输入图像的空间维度)。
特征图是指经过某个卷积层或池化层等处理后得到的输出张量,
其形状可能是 [B, C'', H'', W''],其中 C''、H'' 和 W'' 取决于该层的参数和输入数据的形状。
例如,如果有一个卷积层具有 16 个 3x3 的卷积核,
步长为 1,填充为 1,那么对于输入形状为 [B, 3, H, W] 的图像,
该卷积层的输出特征图形状将是 [B, 16, H, W]
(假设高度和宽度没有因为步长或填充而改变,这在实际中可能不成立,因为边界效应可能会导致尺寸变化)。
欺诈交易
一个交易是否为欺诈是基于历史交易的
将列的个数作为C
取最近n条连续的交易记录,作为一个周期的数据
最近一条记录为欺诈,则确定整个周期的标签为欺诈;否则为正常
这只是个人的一个猜测.... 实际上交易数据常用的转化方法为下面的方法
- L对应列,C=1
mytab('ultitle1', 'tbtitle1');
多尺度
盲人摸象
我们没有一个全局的视角,可以看见事物的全貌
事物的本质一直在我们面前,我们由于种种原因,只能“看到”它的一面(并且大脑总是“骗”我们说这就是全部...)
就像盲人摸象一样,大象像什么? 盲人摸到什么就说像什么什么...
小时候就知道...哈哈哈...
对于一个从来没有见到大象的人来说,把盲人各自摸到的结果拼接起来,
就是一个对大象近似的描述
而这种从多个角度提取信息,然后拼接起来描述一个事物的方法,就是AI中的多尺度
卷积处理文本分类
pytorch卷积网络中的数据shape
尽管其他地方特征图理解为整个图像,这里特征图单指特征的shape
[B,特征的维度,特征shape]
就文本而言,单词就是特征,其shape就是序列长度,
于是,卷积处理中文本类批次矩阵shape为 [B,C,L]
对于图像,像素是特征,其shape就是高与宽,
于是,卷积处理中图像类批次矩阵shape为 [B,C,H,W]
卷积网络主要结构
卷积提取特征,全连接分类
卷积在特征维度进行变换,通常是先升后降,不改变特征的shape
特征shape由maxpool或者全连接变换,通常是一直下降,最后降到标签类别个数
算法思想与业务场景的匹配
卷积提取的特征是区域特征,
是提取整体特征中某个/几个指定的区域
至于这几个区域在整体特征shape中的哪个位置并不重要
比如是否在中间,两边,左上还是右下,没有关系
卷积将特征分层,每层参数抑制非指定区域,强化指定区域,
最后每一层剩下的就是指定的特征
其他特征在这个过程中被淡化了
所以所要找的特征在整体中哪个位置不重要,
与其他特征什么顺序也关系不大
此处的业务场景是判断一个评论是正面还是负面,
实际上就是看一看评论中是否有负面单词
有则负,无则正
这就是一个区域依赖问题,
你要骂人,是上来就骂,还是先说赞美一下再骂,
在这个只有正负二类的场景里,就是负面评论
有为1,无为0,那么负样本应该标记为1,正样本标记为0
而根据大方向判断,文本是一个序列,使用循环神经网络处理也应该没问题
RNN具有长度依赖的问题,就是序列越长,丢失信息越多
相对而言,卷积没有这方面的问题
至于效果如何,还得试过才知道
文本向量化
单词转索引
按数据集的格式,读取出一个个文本,进行分词
获取文本中所有的单词,形成一个单词集合,然后编码
将文本对应的单词列表转化为索引列表
文本转矩阵
神经网络训练的时候是批次训练,其shape为[B,C,L]
B,batch_size,就是批次,代表一次要让神经网络学习多少个样本
C,通道数,特征个数,这里指单词的维数,即用多少个数表示一个单词
L,特征shape,这里指单词个数,一段评论的单词个数
这里有个工程化落地的问题:
矩阵中所有向量元素个数一致,不可能有的向量长而有的短
但实际的评论单词个数并不一致,我们要使之统一
常见做法:
取整个数据集的评论单词个数,然后取平均值,
多的舍弃,少的补0
更精细的做法:
当句子长时,直接舍弃,有点一刀切的意思
此时保留关键单词,舍弃非关键单词更合适一些
但增加了处理的复杂度,什么才是业务的关键单词还需要额外地处理
折中方法:
句子的长度可以取比平均值长一些,拼成矩阵
在输入模型时,再将补的0去掉,
但这要求模型算法支持长度不一致的数据
torch中的RNN就支持这一点
卷积处理文本细节
[B,Cin,Lin] --> [B,Cout,Lout] --> [B,2,1]
先将特征个数通过线性变换扩大,
然后再通过损失函数逼近其收缩,
使之趋近于one hot向量这样 数据集中于某个区域/索引位置,其他位置趋于0的这样一个向量
这样卷积就完成分类工作了,
中间就是一系列的线性变换,
大概就是这个数应该映射到那个位置可以让分类更清晰一点,
这个网络结构是人在暴力设计尝试,
其中的参数是计算机在暴力尝试,从一组随机参数开始,
让损失函数变大的参数舍弃,记录让损失函数变小的参数
让特征个数变大之后,整个矩阵包含的信息量暴增,
然后就减少特征shape,这里就是去除一些序列中的单词
二分类是一个比较粗糙的分类,
在这里就是找到那个负面的单词,只要找到一个,就满足条件了
计算机开始不知道哪个单词重要,随机为每个单词初始化一个参数,
使用梯度下降法,每次微调一下这个参数,
也不知道多少次后,量变引发质量,关键单词的系数变大,其他单词的系数趋于0
这样一个参数网络就是我们需要的
最后序列长度就留下一个,
即一个样本得到一个这样的向量:
它类似于one hot向量,数据集中某几个,最好是一个位置
然后我们使用全连接再将这样一个向量映射为一个与标签one hot向量同shape的向量
在层的维度看过去是某个特征消失,留下了指定特征,
在向量维度看过去就是,某些索引位置上的数据趋于0,数据集中于特定的索引位置
这就是神经网络分类的数据流向及变化
TextCNN核心思想
这里说的是那篇TextCNN论文的核心思想
一个单词表达一个意思,
选择最能表达整段文本意思的 相靠近的两个单词,转换为长度为2的向量
选择最能表达整段文本意思的 相靠近的三个单词,转换为长度为2的向量
选择最能表达整段文本意思的 相靠近的四个单词,转换为长度为2的向量
kernel size选多少个单词是ngram的思想
将这三个向量拼接成起来,再全连接分类
多尺度:从多个尺度中的每个尺度提取出一个信息出来,然后拼接分类
序列长度从L直接降为1
TextCNN比较大胆的是,它 使用池化 从卷积的结果中选了一个特征,只选了一个
在常见的神经网络中,[B,Cin,Lin] --> [B,Cout,Lout] --> [B,2,1]
这个特征shape,也就是L,是一直在减小,可以由maxpool来实现,
但这个减少不是一下子从一个数降到了1,而是经历多个网络层层下降,
但TextCNN就一步,直接将它从L变成了1
TextCNN论文实现
原论文复现
原论文优化
原论文复现
import torch
from torch import nn
from torch.nn import functional as F
class TextCNN1(nn.Module):
"""1维卷积实现文本分类
TextCNN论文实现
"""
def __init__(self,num_embeddings, embedding_dim,padding_idx,seq_len):
super().__init__()
self.embed = nn.Embedding(num_embeddings=num_embeddings,
embedding_dim=embedding_dim,
padding_idx=padding_idx)
# [N, C, seq_len] -- [N, C, seq_len-1],[N, C, seq_len-kernel_size+1]
self.gram_2 = nn.Sequential(
nn.Conv1d(in_channels=embedding_dim, out_channels=2, kernel_size=2),
# 因为kernel为2,又没有补,所以卷积后序列长度减1
nn.MaxPool1d(kernel_size=seq_len-1) # [N, C, 1]
)
# [N, C, seq_len, 1] -- [N, C, seq_len-2, 1]
self.gram_3 = nn.Sequential(
nn.Conv1d(in_channels=embedding_dim, out_channels=2, kernel_size=3),
# 因为kernel为3,又没有补,所以卷积后序列长度减2
nn.MaxPool1d(kernel_size=seq_len-2) # [N, C, 1]
)
# [N, C, seq_len, 1] -- [N, C, seq_len-3, 1]
self.gram_4 = nn.Sequential(
nn.Conv1d(in_channels=embedding_dim, out_channels=2, kernel_size=4),
nn.MaxPool1d(kernel_size=seq_len-3) # [N, C, 1]
)
self.dropout1 = nn.Dropout(p=0.2)
self.fc1 = nn.Linear(in_features=2*3, out_features=2)
def forward(self,X):
# [B,seq_len,embedding_dim]
x = self.embed(X)
# [B, seq_len, embedding_dim] -- [B, embedding_dim, seq_len]
x = torch.permute(input=x, dims=(0, 2, 1))
print(x.shape) # torch.Size([128, 256, 87])
x1 = self.gram_2(x)
print(f"x1.shape={x1.shape}") #torch.Size([128, 2, 1])
x2 = self.gram_3(x)
x3 = self.gram_4(x)
x = torch.concat(tensors=(x1,x2,x3),dim=1)
print(x.shape) # torch.Size([128, 6, 1])
x = torch.squeeze(x)
x = self.dropout1(x)
x = self.fc1(x)
return x
原论文优化**
import torch
from torch import nn
from torch.nn import functional as F
class TextCNN3(nn.Module):
"""
TextCNN优化,
"""
def __init__(self,num_embeddings, embedding_dim,padding_idx,seq_len):
super().__init__()
self.embed = nn.Embedding(num_embeddings=num_embeddings,
embedding_dim=embedding_dim,
padding_idx=padding_idx)
# [N, C, seq_len] -- [N, C, seq_len-1],[N, C, seq_len-kernel_size+1]
self.gram_2 = nn.Sequential(
nn.Conv1d(in_channels=embedding_dim, out_channels=256, kernel_size=2),
nn.BatchNorm1d(num_features=256),
nn.ReLU(),
nn.MaxPool1d(kernel_size=seq_len-1) # [N, C, 1]
)
# [N, C, seq_len, 1] -- [N, C, seq_len-2, 1]
self.gram_3 = nn.Sequential(
nn.Conv1d(in_channels=embedding_dim, out_channels=256, kernel_size=3),
nn.BatchNorm1d(num_features=256),
nn.ReLU(),
nn.MaxPool1d(kernel_size=seq_len-2) # [N, C, 1]
)
# [N, C, seq_len, 1] -- [N, C, seq_len-3, 1]
self.gram_4 = nn.Sequential(
nn.Conv1d(in_channels=embedding_dim, out_channels=256, kernel_size=4),
nn.BatchNorm1d(num_features=256),
nn.ReLU(),
nn.MaxPool1d(kernel_size=seq_len-3) # [N, C, 1]
)
self.dropout1 = nn.Dropout(p=0.2)
self.fc1 = nn.Linear(in_features=256*3, out_features=2)
def forward(self,X):
# [B,seq_len,embedding_dim]
x = self.embed(X)
# [B, seq_len, embedding_dim] -- [B, embedding_dim, seq_len]
x = torch.permute(input=x, dims=(0, 2, 1))
# print(x.shape) # torch.Size([128, 256, 87])
x1 = self.gram_2(x)
# print(f"x1.shape={x1.shape}") #torch.Size([128, 256, 1])
x2 = self.gram_3(x)
x3 = self.gram_4(x)
x = torch.concat(tensors=(x1,x2,x3),dim=1)
# print(x.shape) # torch.Size([128, 768, 1])
x = torch.squeeze(x)
x = self.dropout1(x)
x = self.fc1(x)
return x
|
|
|
|
|
|
|
酒店评论索引数据集下载