-
公告:
最近文章
有些场合下一定不要发言
这应该属于自己真实发生过的一些被攻击的例子,在这里整理一下以免以后再犯。
评论之前一定要观察评论区,存在阶级矛盾的场合一定不要发言,因为发言了也没人理解你真实的处境。所以还有一个点就是不要暴露自己的处境。不然总有蠢蛋
在某条某个大龄博士重新考本科上电科大的视频下评论:我想读研但是没钱读
OK,评论区本身就处于对视频中主人公非常嫉妒的那种心理了,比如一些人说这就是有钱,那我这样说的话无疑加剧了事情的严重程度。说明我发言之前的判断也是被情绪带着走的,那么整个评论区群体更加情绪化,对我的评论肯定会不加以调查以及询问背景的情况下直接抨击
这个视频还是某无良新闻号标榜为“正能量”视频,结果评论区属于这种阶级矛盾最激烈的地方。
所以理所当然的被攻击了“是不是考不上?”
持续根据个人经历更新中
以本人真实处境得出的观点
本科生之所以难以就业是因为没有掌握一项技能导致难以被筛选到对应岗位。
而掌握一项技能往往意味着长时间重复性的训练,这种训练往往是长期没有回报的,而行业本身也是动荡的,当长期投入之后终于可以进入行业却发现行业已成为红海那完全是不值得的。
对本人以及身边人的处境来说都是如此,无法在没有金钱回报甚至需要自己投入金钱的时候长期学习一项吃饭的技能。那就只能困在一些不需要高级技能的零碎劳动之中。
或者上岸那种东西。
当然实际上大家都在本科之前已经花大价钱和大精力时间去读书了,却发现读完之后,没有高等教育文凭的话似乎前十二年烟消云散什么都不是。即便有,在没有掌握职业技能的情况下,依旧可以认为是白读的。
灵感【4】
在弹幕视频网站(B)或者社交媒体网站(X),创作者会上传视频或发送含视频的推文。
描改作品作为二创的一种,可以先给点实例,乐曲mv是描改类二创相当多的。
(类比)其实放在三次元就是模仿表演了,和跳鸡舞、华强买瓜具有相似性,虽然ai生成视频用来举例确实不严谨。
当然也可以看出某些差异来。虽然或许有人模仿坤舞发视频,但是很明显的,走其他路线多,坤舞是作为符号出现在视频里。描改不改动作却能成为一大二创种类,这可能和mv的特殊性有关。
因此模仿表演的适合举例应该是其他的,确实会有一些人去做名分镜的场景摄影作品,角色扮演者的活动应该能作为合适类比。
(解析)描改作品的创作方式自然就是描改,工作就是找个原视频和目标人物去换了。从部分描改实例中可以看出,该类作品创作的目的与目标人物相关,利用了原视频的布局、动态还有其他的视觉设计。
发布描改作品的目的可能是推广和引流。在选择目标方面了解不多所以不再深入搭建。不过还是再添加一个只是想要创作的目的上去,以展现粉丝力量为主。
某个创作获得了流量的认可,或者获得了粉丝的认可,描改二创可以广泛的涌现。按理讲很难找到最早发布描改是什么人,毕竟插画干这种活和抄袭一样。可能来自“既然乐曲翻唱那么封面也可以换人拍”这样的说法吧,也和模仿能搭边。
(还是类比)描改二创的逻辑可以是这样的:描改是二创,二创目的是流量(或者推广还是别的啥),某人的二创出圈,证明其中含有成功的要素,那么其他创作者可以提取其中某些标志性元素用于本人作品的引流大计;或者看到某个创作有成功的要素但是不够全,也一样拿来利用(表情包方面表现很明显,因为传播力度明显不如社媒视频)。仅更换人物之类的要素就是描改的用意了。
所谓目的当然可以变,比如单纯因为换个vsinger就重画一遍保持一致也是可以的。
(结束的推理)所以描改是这样的二创,它改变的是原作品画面的内容而非画面的结构,保持视觉感受一致,但是通过要素组成改变而能够获取流量。它获取的是剩余流量,但是剩余是相对于原创作而言,即剩余流量并不代表更少的流量。只是其利用的要素本身潜力越小,组合后的作品流量越不可能大。因此也可以认为那些能设计出高潜力要素的作者,其搭配也会慎重的选择,因此描改难以超越原作。
(扩展)音mad保持画面与音乐的关联,因此原作品当然是音乐。其自由程度相比描改大得多,因为视觉要素需要完全由自己设计。这应当不符合“通过改变要素组成”。它的逻辑由于知道的实例实在很少所以不能理清,只能由经验知道,成功的mad可以是依附名曲的有潜力的小故事,其本质和成功的mv类似。
大众用户生产内容的行为与科研的相似性
大众用户生产内容的行为与科研的相似性
起因是刚才看了一种用户描改的音mad https://www.bilibili.com/video/BV1tA3k6hEG9
我点击分享推给好友之后。在首页推出大量同音乐的描改。
在科研中,有一种科研范式是将某个领域的某种方法在另一个领域尝试使用从而达到科研的目的。
内容生产领域同样如此,想起来一个词应该叫“二创”,不过似乎二创这个词范围过大了。所以不考虑这个。
这种范式在描改领域最为常见且可以认为具有一定的质量与价值。起到了对该音mad所带音乐的宣传作用。
在科研中移花接木也不算什么恶劣的事情,因此本文到此结束。
今天学到一个词叫第一性原理,那其实成为能够第一个提出可以将音乐描改手书范式的人也许是成功的。
但是创作领域似乎一般来说发明家通常无法获取大量关注,热度往往是资本或影响力主带动起来的。
因此模仿也是一种方式。也许对应论文复现。
RMSNorm kernel
RMSNorm kernel
1 Background
kernel表述:
RMSNorm — PyTorch 2.13 documentation
其中,$x$, $y$ 对应输入输出行向量,$\gamma$对应权重向量。
而实际上输入的$x$的shape应该为..., hidden_dim,其中...表示batch_like shape。该计算仅在最后一个维度上进行。在本问题中定义$x$的shape为rows hidden_dim
2 Analytics & Implementation
Abstraction
可以拆解成并行规约与逐元素乘法
并行规约$RMS(x)$
对每行采用并行规约算法计算出平方和,然后在合适的时机取均值加上$\epsilon$开根号获取$RMS$值。
逐元素乘法$x_{i} * \gamma_i * \frac{1}{RMS(x)}$
第二步进行并行逐元素乘法即可。
Arithmentic Intensity
假设数据类型为float32,我们以行为粒度进行计算。定义d = hidden_dim。
For every row, Load line of x, and $\gamma$, which is 2 * d elements.
Write one line of y, which is 1 * d elements.
For one row It has approximately 2 * d(rms) + 2 * d(per-element-multiply) FP operations.
$$
\text{Arithmetic Intensity} = \frac{\text{FOPs}}{MOPs} = \frac{4}{3}
$$
**Implementation**
对于并行规约,可以把每行当做一个BLOCK,进行基于warp_shuffle的规约。
由于还不知道输入的形状分布如何,认为是随机分布的,因此暂时还没有一个fine-grained的实现。
template <typename T>
/**
* @brief fused kernel of RMSNorm
* 每个BLOCK接管一行,行内通过warp_reduce进行求和
* 然后进行后续计算
* @tparam T float
* @param output \sum {x_i ^ 2} upcast to float
* @param input input x
* @param weight weight
* @param eps epsilon of RMSNorm
* @param rows rows
* @param hidden_dim hidden dimension
* @param n element count of input , in this case its hidden_dim
* @return __global__
*/
__global__ void fused_RMSNorm_kernel(T *output, const T *input, const T *weight, float eps, size_t rows, size_t hidden_dim) {
extern __shared__ float smem[];
__shared__ float multiplier;
size_t tid = threadIdx.x;
int rowStartIdx = blockIdx.x * hidden_dim;
float sum = 0;
for (size_t i = tid; i < hidden_dim; i += blockDim.x)
{
int idx = rowStartIdx + i;
sum += (float)input[idx] * (float)input[idx];
}
float warp_sum = warp_reduce(sum);
if (tid % 32 == 0) {
smem[tid / 32] = warp_sum;
}
__syncthreads();
if (tid < 32) {
float block_sum = (tid < (blockDim.x + 31) / 32) ? smem[tid] : float(0);
block_sum = warp_reduce(block_sum);
if(tid == 0) {
multiplier = rsqrt(block_sum / hidden_dim + eps);
}
}
__syncthreads();
#pragma unroll
for (int i = tid; i < hidden_dim; i += blockDim.x) {
int idx = rowStartIdx + i;
output[idx] = (T)((float)input[idx] * multiplier * (float)weight[i]);
}
}
Performance
耗时分布
-------(512,1592)------------
[float]CPU耗时:3728us
[float]GPU耗时:4051us
-------(512,1592)------------
[half]CPU耗时:58268us
[half]GPU耗时:2789us
-------(512,2048)------------
[float]CPU耗时:4960us
[float]GPU耗时:2586us
-------(512,2048)------------
[half]CPU耗时:77099us
[half]GPU耗时:1523us
可以发现半精度确实由于数据加载时间缩短导致耗时降低。
Figure Generated by doubao
可以发现在数据规模较小的时候,由于kernel launch等各种overhead,CPU的实现会比GPU的各种实现更加快。
在大规模的时候,GPU的并行带来的性能才逐渐显现出来,且使用half会比float更加efficiency。
不过读者需要注意到该naiive实现中,RMS的计算会upcast到float32以防止溢出。
3 Optimization
3.1 Vectorize
后面的点乘为逐元素运算,因此可以选择向量化进行加速。
向量化加速的原理其一为缩短指令条数,其二为增大了运算部件的利用率。类似于SIMD的用法。
template<typename T>
/**
* @brief vectorized kernel
* 每个BLOCK接管一行,行内通过warp_reduce进行求和
* 然后进行后续计算
* @tparam T float/half
* @param output \sum {x_i ^ 2} upcast to float
* @param input input x
* @param weight weight
* @param eps epsilon of RMSNorm
* @param rows rows
* @param hidden_dim hidden dimension
* @param n element count of input , in this case its hidden_dim
* @return __global__
*/
__global__ void vec_fused_RMSNorm_kernel(T *output, const T *input, const T *weight, float eps, size_t rows, size_t hidden_dim) {
extern __shared__ float smem_vec[];
__shared__ float multiplier;
size_t tid = threadIdx.x;
int rowStartIdx = blockIdx.x * hidden_dim;
float sum = 0;
for (size_t i = tid; i < hidden_dim; i += blockDim.x)
{
int idx = rowStartIdx + i;
sum += (float)input[idx] * (float)input[idx];
}
float warp_sum = warp_reduce(sum);
if (tid % 32 == 0) { /* each warp, thread 0 takes sum of warp. */
smem_vec[tid / 32] = warp_sum;
}
__syncthreads();
if (tid < 32/* 32 is warpsize */) {
float block_sum_float = (tid < (blockDim.x + 31) / 32) ? smem_vec[tid] : 0;
block_sum_float = warp_reduce(block_sum_float);
if(tid == 0) {
multiplier = rsqrt(block_sum_float / hidden_dim + eps); // Assign row rms multiplier.
}
}
__syncthreads();
if constexpr (std::is_same_v<T, float>) {
const float4 *vec_input = reinterpret_cast<const float4*>(input);
const float4 *vec_weight = reinterpret_cast<const float4*>(weight);
float4 *vec_output = reinterpret_cast<float4*>(output);
int rowStartFloat4 = rowStartIdx / 4;
#pragma unroll
for (int i = tid; i < hidden_dim / 4; i += blockDim.x / 4) {
int idx = rowStartFloat4 + i;
vec_output[idx] = mul(mul(vec_input[idx], float4{multiplier,multiplier,multiplier,multiplier}), vec_weight[i]);
}
} else if constexpr (std::is_same_v<T, half>) {
const half2 *vec_input = reinterpret_cast<const half2*>(input);
const half2 *vec_weight = reinterpret_cast<const half2*>(weight);
half2 *vec_output = reinterpret_cast<half2*>(output);
int rowStartHalf2 = rowStartIdx / 2;
#pragma unroll
for (int i = tid; i < hidden_dim / 2; i += blockDim.x / 2) {
int idx = rowStartHalf2 + i;
vec_output[idx] = mul(mul(vec_input[idx], half2{(half)multiplier,(half)multiplier}), vec_weight[i]);
}
}
}
性能
-------(512,1592)------------
[float]CPU耗时:4115us
[float]GPU耗时:2211us
-------(512,1592)------------
[half]CPU耗时:54678us
[half]GPU耗时:1754us
-------(512,2048)------------
[float]CPU耗时:4696us
[float]GPU耗时:1818us
-------(512,2048)------------
[half]CPU耗时:69593us
[half]GPU耗时:2192us
从数据可以发现确实降低了相当的执行耗时。
从图上分析可知依旧是小batch时float CPU领先,General Case时GPU是一个trade-off选择。