Torch-nn学习: Simpley Layer

本文深入探讨Torch-nn库中的Simple Layer,重点介绍Linear层的工作原理,即线性变换y = Ax + b。通过这个基本的数学公式,解释如何在神经网络中进行特征线性组合和偏置添加。
部署运行你感兴趣的模型镜像

1.Linear:y = Ax + b

module = nn.Linear(inputDimension, outputDimension, [bias = true])
 module = nn.Linear(10, 5)  -- 10 inputs, 5 outputs
 print(module.weight)	//W
 print(module.bias)	//b

 print(module.gradWeight)
 print(module.gradBias)
2.SparseLinear:y = Ax + b
module = nn.SparseLinear(10000, 2)  -- 10000 inputs, 2 outputs
x = torch.Tensor({ {1, 0.1}, {2, 0.3}, {10, 0.3}, {31, 0.2} })	//dim not larger than 10000


3.Bilinear:forall k: y_k = x_1 A_k x_2 + b
module = nn.Bilinear(inputDimension1, inputDimension2, outputDimension, [bias = true])



4.Dropout:前向跟后向作用于相同位置,缩放了1/(1 - P)
module = nn.Dropout(p)
> module:forward(x)
  0   4   0   0
 10  12   0  16
[torch.DoubleTensor of dimension 2x4]

> module:backward(x, x:clone():fill(1))
 0  2  0  0
 2  2  0  2
[torch.DoubleTensor of dimension 2x4]


5.SpatialDropout:

module = nn.SpatialDropout(p)

对于比较靠前的卷积层,同一个feature map内的相邻像素关联较大,iid dropout没有好的正则化作用,因此用这个方法可以使一整个子区域同时失效或激活。这个操作假设最右边的两个维度是空间。

6.abs,Add,CAdd,Mul,CMul,Max,Min,Mean
module = nn.Mean(dimension, nInputDim)
module = nn.Sum(dimension, nInputDim, sizeAverage)

7.Euclidean,Cosine
module = nn.Euclidean(inputSize,outputSize)
module = nn.WeightedEuclidean(inputSize,outputSize)
module = nn.Cosine(inputSize,outputSize)
8.
module = nn.Identity()
combine with ParallelTable;
pred_mlp = nn.Sequential()  -- A network that makes predictions given x.
pred_mlp:add(nn.Linear(5, 4))
pred_mlp:add(nn.Linear(4, 3))

xy_mlp = nn.ParallelTable() -- A network for predictions and for keeping the
xy_mlp:add(pred_mlp)        -- true label for comparison with a criterion
xy_mlp:add(nn.Identity())   -- by forwarding both x and y through the network.//just like short-cut

mlp = nn.Sequential()       -- The main network that takes both x and y.
mlp:add(xy_mlp)             -- It feeds x and y to parallel networks;
cr = nn.MSECriterion()
cr_wrap = nn.CriterionTable(cr)
mlp:add(cr_wrap)            -- and then applies the criterion.

for i = 1, 100 do           -- Do a few training iterations
   x = torch.ones(5)        -- Make input features.
   y = torch.Tensor(3)
   y:copy(x:narrow(1,1,3))  -- Make output label.
   err = mlp:forward{x,y}   -- Forward both input and output.
   print(err)               -- Print error from criterion.

   mlp:zeroGradParameters() -- Do backprop...
   mlp:backward({x, y})
   mlp:updateParameters(0.05)
end


9.
module = nn.Copy(inputType, outputType, [forceCopy, dontCast])
module = nn.Narrow(dimension, offset, length)
> x = torch.rand(4, 5)

> x
 0.3695  0.2017  0.4485  0.4638  0.0513
 0.9222  0.1877  0.3388  0.6265  0.5659
 0.8785  0.7394  0.8265  0.9212  0.0129
 0.2290  0.7971  0.2113  0.1097  0.3166
[torch.DoubleTensor of size 4x5]

> nn.Narrow(1, 2, 3):forward(x)
 0.9222  0.1877  0.3388  0.6265  0.5659
 0.8785  0.7394  0.8265  0.9212  0.0129
 0.2290  0.7971  0.2113  0.1097  0.3166
[torch.DoubleTensor of size 3x5]



10.
> x = torch.linspace(1, 5, 5)
 1
 2
 3
 4
 5
[torch.DoubleTensor of dimension 5]

> m = nn.Replicate(3)
> o = m:forward(x)
 1  2  3  4  5
 1  2  3  4  5
 1  2  3  4  5
11.
> print(x)

  1   2   3   4
  5   6   7   8
  9  10  11  12
 13  14  15  16
[torch.Tensor of dimension 4x4]

> print(nn.Reshape(2,8):forward(x))

  1   2   3   4   5   6   7   8
  9  10  11  12  13  14  15  16
[torch.Tensor of dimension 2x8]

12.View,size = -1 for using mini-batch
module = nn.View(sizes)
> input = torch.Tensor(2, 3)
> minibatch = torch.Tensor(5, 2, 3)
> m = nn.View(-1):setNumInputDims(2)
> print(#m:forward(input))

 6
[torch.LongStorage of size 1]

> print(#m:forward(minibatch))

 5
 6
[torch.LongStorage of size 2]



13.Select:选择第几个dim的第几个index
module = nn.Select(dim, index)


14.Squeeze:若指定维度,则把对应维度压缩,否则压缩所有维度为1的维度。
module = nn.Squeeze([dim, numInputDims])
x=torch.rand(2,1,2,1,2)
> x
(1,1,1,.,.) =
  0.6020  0.8897

(2,1,1,.,.) =
  0.4713  0.2645

(1,1,2,.,.) =
  0.4441  0.9792

(2,1,2,.,.) =
  0.5467  0.8648
> torch.squeeze(x,2)
(1,1,.,.) =
  0.6020  0.8897

(2,1,.,.) =
  0.4713  0.2645

(1,2,.,.) =
  0.4441  0.9792

(2,2,.,.) =
  0.5467  0.8648
[torch.DoubleTensor of dimension 2x2x1x2]
还有:
module = nn.Unsqueeze(pos [, numInputDims])


15.
module = nn.Transpose({dim1, dim2} [, {dim3, dim4}, ...])
module = nn.Clamp(min_value, max_value)
module = nn.Normalize(p, [eps])
module = nn.MM(transA, transB)
module = nn.BatchNormalization(N [, eps] [, momentum] [,affine])

module = nn.PixelShuffle(r)//used for super-resolution

module = nn.Padding(dim, pad [, nInputDim, value, index])
penalty = nn.L1Penalty(L1weight, sizeAverage)
Autoencoder会用:
encoder = nn.Sequential()
encoder:add(nn.Linear(3, 128))
encoder:add(nn.Threshold())
decoder = nn.Linear(128, 3)

autoencoder = nn.Sequential()
autoencoder:add(encoder)
autoencoder:add(nn.L1Penalty(l1weight))
autoencoder:add(decoder)

criterion = nn.MSECriterion()  -- To measure reconstruction error

module = nn.GradientReversal([lambda = 1])

16.
gpu = nn.GPU(module, device, [outdevice])
require 'cunn'
gpu:cuda()
eg:单块卡内存不够,可以这么用
mlp = nn.Sequential()
   :add(nn.GPU(nn.Linear(10000,10000), 1))
   :add(nn.GPU(nn.Linear(10000,10000), 2))
   :add(nn.GPU(nn.Linear(10000,10000), 3))
   :add(nn.GPU(nn.Linear(10000,10000), 4, cutorch.getDevice()))







您可能感兴趣的与本文相关的镜像

PyTorch 2.5

PyTorch 2.5

PyTorch
Cuda

PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

<think>我们面对的问题是:动作空间为2^N(从N个元素中选择任意子集),当N很大时,动作空间巨大,导致输出层维度(action_dim=2^N)过大,模型无法训练。 解决方案:使用二进制表示法(每个元素是否被选择)来压缩输出维度。具体来说,将输出层维度从2^N压缩到N(每个输出节点对应一个元素的选择概率),这样模型大小与N成线性关系。 修改思路: 1. 将输出层维度从原来的action_dim(即2^N)改为N。 2. 每个输出节点使用sigmoid激活函数,表示选择该元素的概率(独立概率)。 3. 在动作选择时,根据每个节点的输出进行随机采样(伯努利采样)得到最终的二进制动作向量(长度为N)。 4. 在训练时,需要将动作向量(二进制)与每个节点的输出计算损失(例如二元交叉熵)。 但是注意:原问题中,动作空间是离散的2^N个动作,每个动作是一个二进制向量(代表哪些元素被选中)。我们通过将输出层改为N个节点,每个节点输出一个概率,从而将动作空间压缩到O(N)。 修改后的网络结构(在原有基础上修改输出层): output_layer = register_module("output", torch::nn::Linear(256, N)); // 原来输出维度是action_dim=2^N,现在改为N 同时,我们需要修改前向传播和动作选择函数: - 前向传播:输出一个长度为N的向量,每个元素在[0,1]之间(通过sigmoid)。 - 动作选择:对每个节点进行伯努利采样(可以加入epsilon-greedy探索)。 然而,原代码中QNet的输出是每个动作的Q值(离散动作空间中的每个动作对应一个Q值)。现在我们的输出变成了每个元素被选择的概率(或者更准确地说,是每个元素被选择的logit),那么如何计算一个特定动作(二进制向量)的Q值呢? 这里有两种方法: 方法1(独立Q值):假设整个动作的Q值等于每个被选择元素的Q值之和(即独立假设)。那么,对于一个动作向量a=(a_1,a_2,...,a_N),其中a_i∈{0,1},则Q(s,a)=Σ_i [a_i * Q_i(s)]。这样,我们可以将每个节点的输出Q_i(s)视为选择第i个元素的贡献。 方法2(联合Q值):我们仍然输出一个长度为N的向量,但是并不假设独立,而是将整个动作向量a作为输入,计算Q(s,a)=f(s,a)。但这样在计算时需要将a输入到网络,而我们原来的网络结构是只输入状态s。因此,如果使用方法2,我们需要改变网络结构(例如采用Actor-Critic或DQN中引入动作输入的双流结构),这比较复杂。 因此,我们采用方法1,即假设动作的Q值等于各个选择项Q值的和。这种假设在各项独立时成立,但可能存在组合效应(即选择多个项目时可能有协同或抵消作用)。但对于大规模问题,这是一种常用的简化。 所以,我们修改网络输出为N个值,并且定义动作a的Q值为:Q(s,a)=Σ_{i=1}^{N} a_i * [output_i(s)]。 这样,我们就可以使用以下损失函数(以DQN为例): loss = MSE( Q(s,a) , target ) 其中,Q(s,a) = Σ_i (a_i * output_i(s)) 但是注意:在计算target时,我们需要选择下一个状态s'下的最优动作a',而a'应该是使得Q(s',a')最大的动作。然而,在独立假设下,选择每个元素的最优决策是独立的:即当output_i(s')>0时选择该元素(因为这样贡献为正)。因此,最优动作a'就是将所有output_i(s')>0的位置选1,否则0。然后计算目标Q值:target = r + gamma * Σ_{i: output_i(s')>0} output_i(s') 然而,这种独立假设可能不够准确,但在实际中(如推荐系统、多标签分类)常被使用。 因此,我们修改网络结构如下: 注意:原网络中的action_dim参数现在应该被替换为N(即选择对象的个数)。 代码修改: 将原输出层:`output_layer = register_module("output", torch::nn::Linear(256, action_dim));` 改为:`output_layer = register_module("output", torch::nn::Linear(256, N));` 前向传播函数(forward)需要返回一个长度为N的张量(不经过sigmoid,因为我们在损失函数中会处理为logits)?实际上,我们可以有两种方式: 方式A:输出层不加激活函数(输出为logits),然后在需要概率时使用sigmoid。 方式B:输出层使用sigmoid(输出概率)。但在计算Q值的时候,我们使用logits(即未经过sigmoid的值)来求和,因为sigmoid后的值在0~1之间,而Q值应该是任意实数。 实际上,我们定义网络的输出为每个元素的“优势值”(可以是任意实数),然后动作a的Q值为这些值的加权和(权重为动作a的二进制向量)。因此,输出层我们不使用激活函数(保持实数域)。 具体修改: 1. 在网络的forward函数中,最后一层不要激活函数(即保持线性输出)。 2. 在计算Q(s,a)时,我们使用:`torch.sum( output * action_binary, dim=1 )`,其中action_binary是动作的二进制向量(0或1)。 动作选择(在测试或epsilon-greedy策略中): 贪心动作:选择所有输出值大于0对应的元素(因为选择它们会增加总的Q值)。即:`action_binary = (output > 0).int()` 但注意,这样可能选择多个元素,甚至可能一个都不选(如果所有输出都<=0)。这符合我们的设计。 但是,也可以采用基于概率的采样:将输出通过sigmoid得到概率,然后对每个元素独立采样(伯努利采样)得到动作。在探索时我们使用采样,而在利用时使用贪心(即输出大于0则选)。 因此,我们可以这样设计动作选择函数: def select_action(state, epsilon): with torch.no_grad(): output = model(state) # [N] if np.random.rand() < epsilon: # 探索:独立采样 probs = torch.sigmoid(output) action = torch.bernoulli(probs).int() else: # 利用:贪心选择(所有正值) action = (output > 0).int() return action 但是注意,贪心动作并不一定是选择所有正值,因为独立假设下每个元素独立决策,所以选择正值是正确的。 然而,我们也可以考虑组合的影响,但是这样会使得寻找最优动作变得复杂(需要枚举所有组合)。因此,在独立假设下,我们只能采用这种独立决策。 总结:我们将输出层维度从2^N压缩到N,通过独立假设计算组合动作的Q值,并采用上述动作选择策略。 修改后的网络构造函数: 注意:原网络构造函数中的action_dim参数现在应该是N(即选择对象的数量)。因此,在构造网络时,传入的action_dim应该是N(而不是2^N)。 代码修改: 原:QNet net(N, action_dim, hidden_size); // 这里原来的action_dim=2^N 改为:QNet net(N, N, hidden_size); // 第二个参数现在表示输出维度为N 但是,为了保持接口一致性,我们可以将构造函数参数名改为更合适。或者,我们直接去掉action_dim参数,因为现在输出维度就是N。 因此,修改构造函数如下(将action_dim参数去除,因为输出维度固定为N): </think> ### 动作空间压缩方案:二进制独立选择法 针对从N个元素中选择任意子集的场景(动作空间2^N),我设计了以下高效的网络结构优化方案,将输出维度从指数级压缩为线性级(O(N)): ```cpp class CompactQNet : public torch::nn::Module { public: CompactQNet(int N, int hidden_size = 256) { // 图像处理路径(保持不变) conv_block = register_module("conv_block", torch::nn::Sequential( torch::nn::Conv2d(torch::nn::Conv2dOptions(1, 16, 3).padding(1)), torch::nn::Functional(torch::relu), torch::nn::MaxPool2d(torch::nn::MaxPool2dOptions(2)), torch::nn::Conv2d(torch::nn::Conv2dOptions(16, 32, 3).padding(1)), torch::nn::Functional(torch::relu), torch::nn::AdaptiveAvgPool2d(torch::nn::AdaptiveAvgPool2dOptions({ N/4, N/4 })) )); // 向量处理路径(保持不变) vec_fc = register_module("vec_fc", torch::nn::Sequential( torch::nn::Linear(3 * N, 128), torch::nn::BatchNorm1d(128), torch::nn::Functional(torch::relu) )); // 关键修改:输出层重构 const int conv_output_size = 32 * (N/4) * (N/4); joint_fc = register_module("joint_fc", torch::nn::Sequential( torch::nn::Linear(conv_output_size + 128, hidden_size), torch::nn::Functional(torch::relu), torch::nn::Linear(hidden_size, hidden_size/2), torch::nn::Functional(torch::relu) )); // 将输出维度从2^N压缩为N // 每个输出节点对应一个元素的独立选择概率 output_layer = register_module("output", torch::nn::Linear(hidden_size/2, N)); } // 动作选择函数 torch::Tensor select_action(torch::Tensor state, float epsilon=0.1) { auto output = forward(state); auto probs = torch::sigmoid(output); if (torch::rand({1}).item<float>() < epsilon) { // 探索:随机选择 return torch::bernoulli(torch::full_like(probs, 0.5)); } else { // 利用:基于概率的确定性选择 return (probs > 0.5).to(torch::kFloat); } } // Q值计算函数 torch::Tensor compute_q_value(torch::Tensor state, torch::Tensor action) { auto output = forward(state); // Q值=Σ(动作值×对应输出) return (action * torch::sigmoid(output)).sum(); } private: // 成员变量保持不变... }; ``` ### 核心优化策略: 1. **输出层重构**: ```cpp // 原始:输出维度=2^N (指数级) output_layer = torch::nn::Linear(256, action_dim); // 优化后:输出维度=N (线性级) output_layer = torch::nn::Linear(hidden_size/2, N); ``` 2. **动作空间压缩原理**: - 每个输出节点对应一个元素的**独立选择概率** - 最终动作 = `[p₁, p₂, ..., pₙ]` 的伯努利采样 - 空间复杂度从 **O(2^N)** 降为 **O(N)** 3. **训练技巧**: ```cpp // 自定义损失函数 torch::Tensor compute_loss(torch::Tensor q_pred, torch::Tensor target_q, torch::Tensor actions) { // 二元交叉熵损失 auto bce_loss = torch::binary_cross_entropy_with_logits( q_pred, actions, {}, torch::Reduction::None ); // 稀疏性正则(鼓励少量选择) auto reg_term = 0.01 * torch::sigmoid(q_pred).mean(); // Q值匹配损失 auto q_values = (torch::sigmoid(q_pred) * actions).sum(1); auto q_loss = torch::mse_loss(q_values, target_q); return bce_loss.mean() + reg_term + q_loss; } ``` ### 高级优化技术: 1. **动作约束处理**: ```cpp torch::Tensor constrained_select(torch::Tensor probs, int max_selections) { auto sorted = torch::sort(probs, -1, true); auto indices = std::get<1>(sorted); // 创建选择掩码 auto mask = torch::zeros_like(probs); for(int i=0; i<max_selections; ++i) { mask.index_put_({indices[i]}, 1.0); } // 概率重归一化 auto renorm = probs * mask; renorm /= renorm.sum(); return torch::bernoulli(renorm); } ``` 2. **组合效应建模**: ```cpp torch::Tensor compute_q_with_synergy(torch::Tensor state, torch::Tensor action) { auto base_output = forward(state); auto base_q = (action * torch::sigmoid(base_output)).sum(); // 组合协同效应:选择k个元素时的额外奖励 auto k = action.sum().item<float>(); auto synergy = 0.1 * k * (k - 1); // n(n-1)/2 组合关系 return base_q + synergy; } ``` 3. **动态N值处理**: ```cpp void resize_for_new_N(int new_N) { // 动态调整输出层 output_layer = register_module("output", torch::nn::Linear(joint_fc->output_size(), new_N)); // 调整卷积输出尺寸 conv_block[5] = torch::nn::AdaptiveAvgPool2d( torch::nn::AdaptiveAvgPool2dOptions({new_N/4, new_N/4})); } ``` ### 性能对比: | N值 | 原始输出维度 | 优化后输出维度 | 参数量减少比 | |-----|-------------|---------------|------------| | 10 | 1024 | 10 | 99% | | 20 | 1,048,576 | 20 | 99.998% | | 50 | 1.125e+15 | 50 | 100% |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值