costreamjs
Version:
A high-performance streaming programming language for parallel architecture. This repo (js-version) is created for better using & reading & debugging.
1,079 lines (1,013 loc) • 45.3 kB
JavaScript
import { UnfoldComposite, compositeCallFlow } from "./unfoldComposite"
import { COStreamJS } from "./global"
import { addNode, parenNode, forNode, compositeCallNode, splitjoinNode, pipelineNode, ComInOutNode, compHeadNode, compBodyNode, compositeNode, binopNode, operatorNode, splitNode, roundrobinNode, duplicateNode, joinNode, constantNode, blockNode, declareNode, operBodyNode, winStmtNode, declarator, idNode, inOutdeclNode, strdclNode, unaryNode, conv2DLayerNode, maxPooling2DLayerNode, activationLayerNode } from "../ast/node";
import { top, setTop } from "./global"
import { SymbolTable, Variable } from "./symbol";
import { sequentialNode, denseLayerNode, layerNode, averagePooling2DLayerNode } from "../ast/node";
import { error } from "../utils";
/**
* 对于如下形式的 squential 和 Dense 的例子
* Out = squential (In, Y) (784) {
* add Dense(100);
* add Dense(10);
* };
* 我们要连接数据流节点的策略是: 以 loss 为中心, 前后对称地补上 dense 和 dDense , 最后在首部加一个 copy
* 我们要生成的 composite 的样式为
* composite sequential_0(input stream<double x>In, stream<double x>Y, output stream<double x> Out){
* stream<double x> copy_1, copy_2, F1_F2, F1_B2, F2_loss, _Loss, B2_B1, Out;
* (copy_1,copy2) = copy(In); // 内容参见 MakeCopyOperator
(F1_F2,F1_B2)=dense_1(copy_1)();
F2_loss=dense_2(F1_F2)();
_Loss=loss(F2_loss,Y)();
B2_B1=dDense_2(_Loss,F1_B2)();
Out=dDense_1(B2_B1,copy_2)();
* }
* 将该新生成的 composite 加入 COStreamJS.ast 以及符号表的 S.compTable 中
* 然后我们要返回的 compositeCallNode 的样式为
* Out = sequential_0(In,Y);
*
* @param {sequentialNode} node
* @returns {compositeCallNode}
*/
UnfoldComposite.prototype.UnfoldSequential = function (node) {
setTop(new SymbolTable(top, null)) // 对生成的新 composite 构建新的符号表
let compName = this.MakeCompositeName("squential");
let call_list = compositeCallFlow(node.body_stmts);
const strType = top.prev.streamTable[node.inputs[0]].strType // 这里也简单默认输入输出数据流类型一致, 若有不一致的需求, 应修改此处代码
const head_inputs = [new inOutdeclNode(null, strType, "In"), new inOutdeclNode(null, strType, "Y")]
const head_outputs = [new inOutdeclNode(null, strType, "Out")]
let inout = new ComInOutNode(null, head_inputs, head_outputs)
let head = new compHeadNode(null, compName, inout) // 构建头部完成
let stmt_list = this.generateSequentialBodyStmts(compName, node, call_list);
let body = new compBodyNode(null, null, stmt_list)
let sequential = new compositeNode(null, head, body) // 已生成该新的 compositeNode
// 将新生成的 compositeNode 插回到语法树和符号表中
COStreamJS.ast.push(sequential)
COStreamJS.S.compTable[compName] = { composite: sequential };
// 构造 compositeCallNode
const compositeCall = new compositeCallNode(null, compName, node.inputs)
compositeCall.outputs = node.outputs
setTop(top.prev) // 还原至上层符号表
return compositeCall
}
/**
* 对于如下形式的 squential 和 Dense 的例子
* Out = squential (In, Y) (784) {
* add Dense(100);
add Dense(10);
};
* 我们要生成的 stmt_list 的格式为{
* @param {sequentialNode} sequential
* @param {layerNode[]} layers
* @returns {statement[]}
*/
UnfoldComposite.prototype.generateSequentialBodyStmts = function (compName, sequential, layers) {
const result = []
let currentLevel = 0 /** 当前层级计数器, 用于数据流名的构造 */
// 0. 将层连接起来
for (let i = 0; i < layers.length - 1; i++) {
layers[i].level = ++currentLevel
layers[i].nextLayer = layers[i + 1]
layers[i + 1].prevLayer = layers[i]
}
layers[layers.length - 1].level = ++currentLevel
// 1. 确定每一层的输入输出规模 执行完后, this.rows 有值了
layers.forEach(layer => layer.init(sequential));
// 2. 在语法树的头部插入权值矩阵 二维数组的声明 例如_weight_0[784][100], _weight_1[100][10]
for (let layer of layers) {
const weightName = '_weight_' + layer.level
switch (layer.constructor) {
case denseLayerNode: {
// 全局声明 权值矩阵 double _weight_[prevDim][dim];
const declStr = `double ${weightName}[${layer.rows}][${layer.cols}];`
const declare = COStreamJS.parser.parse(declStr)[0] // 这里使用了parse字符串的方式来创建了语法树节点. 在 c++ 对应的地方要手动构建
COStreamJS.ast.unshift(declare);
const variable = new Variable('double', weightName, undefined)
variable.shape = [layer.rows, layer.cols];
COStreamJS.S.variableTable[weightName] = variable
break
}
case conv2DLayerNode: {
// 全局声明 权值矩阵 double _weight_[filters][depth][rows][cols];
const depth = layer.inputSize[layer.inputSize.length-1]
const [rows, cols] = layer.kernel_size
const declStr = `double ${weightName}[${layer.filters}][${depth}][${rows}][${cols}];`
const declare = COStreamJS.parser.parse(declStr)[0] // 这里使用了parse字符串的方式来创建了语法树节点. 在 c++ 对应的地方要手动构建
COStreamJS.ast.unshift(declare);
const variable = new Variable('double', weightName)
variable.shape = [layer.filters,depth,rows,cols]
COStreamJS.S.variableTable[weightName] = variable
break;
}
default: break;
}
}
// 3.
// 声明stream stream<double x>...
const strType = new strdclNode(null, 'double', 'x')
const streamDecl = new declareNode(null, strType, ['copy_1', 'copy_2']) // stream<double x>copy_1,copy_2;
result.push(streamDecl)
result.push(this.MakeCopyOperator())
// 用于存储前向传播给反向传播的数据流
// 输入sequential的训练集在反向传播中仍然需要
const temp_stream_list = [['copy_2']]
let temp_stream = ['copy_1']
// 展开前向传播composite
for (let layer of layers) {
let call_inputs = [], call_outputs = []
if (layer !== layers[layers.length - 1]) { // 如果不是最后一个 layer
const namePrefix = 'F' + layer.level + '_' // 前缀, 例如 F1_
// 正向传递给下一层的stream名称, 例如 F1_F2
const tempName1 = namePrefix + 'F' + layer.nextLayer.level
// 将数据流声明加入
streamDecl.init_declarator_list.push(tempName1)
call_inputs = [temp_stream[0]]
if (layer.nextLayer instanceof averagePooling2DLayerNode || layer.nextLayer instanceof activationLayerNode) {
call_outputs = [tempName1]
} else {
// 传递给反向传播中本层的stream名称, 例如 F1_B2
const tempName2 = namePrefix + 'B' + layer.nextLayer.level
streamDecl.init_declarator_list.push(tempName2)
call_outputs = [tempName1, tempName2]
temp_stream_list.push([tempName2])
}
temp_stream.pop()
temp_stream.push(call_outputs[0])
} else { // 如果是最后一个 layer
/*
* 训练过程
正向传播的最后一层不同于其他层,只有一个输出流: call_inputs = new list<Node *>({temp_stream->front()});
* 测试过程
只有正向传播的时候, output为输出:call_outputs = new list<Node *>({outputs->front()});
*/
const tempName = 'F' + layer.level + '_loss'
call_inputs = [temp_stream[0]]
call_outputs = [tempName]
temp_stream.pop()
temp_stream.push(tempName)
streamDecl.init_declarator_list.push(tempName)
}
if(layer instanceof activationLayerNode){
const tempName3 = `F${layer.level}_B${layer.level}`
streamDecl.init_declarator_list.push(tempName3)
call_outputs.push(tempName3)
}
// 构造实际的正向传播composite
const comp = MakeForwardComposite(layer, call_outputs.length == 1)
const call = new compositeCallNode(null, comp.compName, call_inputs)
call.outputs = call_outputs
result.push(new binopNode(null, new parenNode(null,call_outputs), '=', call))
}
debugger;
// dl/dy的输入为y, y`
// 展开反向传播composite, 最后一层的composite的输入为实际预测和期望预测的输入流 也即temp_stream和 与y_stream
const call_inputs = [temp_stream[0], 'Y'], call_outputs = ['_Loss']
streamDecl.init_declarator_list.push('_Loss')
const loss_comp = MakeLossComposite(layers[layers.length - 1])
const loss_call = new compositeCallNode(null, loss_comp.compName, call_inputs)
loss_call.outputs = call_outputs
result.push(new binopNode(null, call_outputs, '=', loss_call))
// 正向传播展开完毕
// 开始展开反向传播
temp_stream = ['_Loss']
for (let layer of layers.slice().reverse()) {
let call_inputs, call_outputs
if (layer instanceof averagePooling2DLayerNode) {
call_inputs = [temp_stream[0]]
}else if(layer instanceof activationLayerNode){
call_inputs = [temp_stream[0], `F${layer.level}_B${layer.level}`]
}else {
temp_stream_list[temp_stream_list.length - 1].unshift(temp_stream[0])
call_inputs = temp_stream_list.pop()
}
if (layer !== layers[0]) {
const namePrefix = 'B' + layer.level + '_'
const tempName = namePrefix + 'B' + layer.prevLayer.level // 例如 B2_B1
call_outputs = [tempName]
} else {
call_outputs = ['Out']
}
if(call_outputs[0] !== 'Out') streamDecl.init_declarator_list.push(call_outputs[0])
temp_stream = [call_outputs[0]]
const back_comp = MakeBackComposite(layer)
const back_call = new compositeCallNode(null, back_comp.compName, call_inputs)
back_call.outputs = call_outputs
result.push(new binopNode(null, new parenNode(null,call_outputs), '=', back_call))
}
// 反向传播展开完毕
debugger;
return result;
}
/**
* 返回一个将输入数据流拷贝2份的 operator
* (copy_1, copy_2) = _copy(In){
* work{
* copy_1[0].x = In[0].x;
* copy_2[0].x = In[0].x;
* }
* window{
* In sliding(1,1);
* copy_1 tumbling(1);
* copy_2 tumbling(1);
* }
* }
* @returns {binopNode}
*/
UnfoldComposite.prototype.MakeCopyOperator = function () {
/** @type {compositeNode} */
const composite = COStreamJS.parser.parse(`
composite copy(input stream<double x>In, output stream<double x>copy_1, stream<double x>copy_2){
(copy_1, copy_2) = _copy(In){
work{
copy_1[0].x = In[0].x;
copy_2[0].x = In[0].x;
}
window{
In sliding(1,1);
copy_1 tumbling(1);
copy_2 tumbling(1);
}
};
}`)[0]
return composite.body.stmt_list[0]
}
/** @returns {compositeNode} */
function MakeForwardComposite(/** @type {layerNode} */layer, singleOutput) {
let comp;
if (layer instanceof denseLayerNode) {
comp = MakeDenseComposite(layer, singleOutput)
}else if(layer instanceof conv2DLayerNode) {
comp = MakeConv2DComposite(layer, singleOutput)
}else if(layer instanceof maxPooling2DLayerNode){
comp = makeMaxPooling2DLayer(layer, singleOutput)
}else if(layer instanceof averagePooling2DLayerNode){
comp = makeAveragePooling2DLayer(layer, singleOutput)
}else if(layer instanceof activationLayerNode){
comp = makeActivationLayer(layer)
}
// 加入符号表
COStreamJS.S.compTable[comp.compName] = { composite: comp }
COStreamJS.ast.push(comp)
return comp
}
/* 构建如下的 dense 层的 composite, 其中需要处理 level 和输出输出窗口大小. 构建完成后加入符号表
*
composite dense_1(input stream<double x>In, output stream<double x>Out0, stream<double x>Out1) {
(Out0,Out1) = dense_1(In){
init{
int i,j;
for(i=0;i<784;i++){
for(j=0;j<100;j++){
_weight_1[i][j]=0;
}
}
}
work{
int i,j;
double temp;
for(j=0;j<100;j++){
temp = 0;
for(i=0;i<784;i++){
temp += In[i].x * _weight_1[i][j] ;
}
Out0[j].x = temp;
Out1[j].x = temp;
}
}
window{
In sliding(784,784);
Out0 tumbling(100,100);
Out1 tumbling(100,100);
}
};
}
*/
function MakeDenseComposite(/** @type {denseLayerNode} */layer, singleOutput = false) {
const { level, rows, cols } = layer
if (singleOutput) {
var compStr = `composite dense_${level}(input stream<double x>In, output stream<double x>Out) {
Out = dense_${level}(In){
init{
int i,j;
for(i=0;i<${rows};i++){
for(j=0;j<${cols};j++){
_weight_${level}[i][j]= random() - 0.5;
}
}
}
work{
int i,j;
double temp;
for(j=0;j<${cols};j++){
temp = 0;
for(i=0;i<${rows};i++){
temp += In[i].x * _weight_${level}[i][j] ;
}
Out[j].x = temp;
}
}
window{
In sliding(${rows},${rows});
Out tumbling(${cols},${cols});
}
};
}`
} else {
var compStr = `composite dense_${level}(input stream<double x>In, output stream<double x>Out0, stream<double x>Out1) {
(Out0,Out1) = dense_${level}(In){
init{
int i,j;
for(i=0;i<${rows};i++){
for(j=0;j<${cols};j++){
_weight_${level}[i][j]=0.01;
}
}
}
work{
int i,j;
double temp;
for(j=0;j<${cols};j++){
temp = 0;
for(i=0;i<${rows};i++){
temp += In[i].x * _weight_${level}[i][j] ;
}
Out0[j].x = temp;
Out1[j].x = temp;
}
}
window{
In sliding(${rows},${rows});
Out0 tumbling(${cols},${cols});
Out1 tumbling(${cols},${cols});
}
};
}`
}
return COStreamJS.parser.parse(compStr)[0]
}
/** @returns {compositeNode} */
function MakeConv2DComposite(/** @type {conv2DLayerNode} */ layer, singleOutput){
const conv2D_comp = MakeConv2DKernel(layer)
COStreamJS.S.compTable[conv2D_comp.compName] = { composite: conv2D_comp }
COStreamJS.ast.push(conv2D_comp)
if(singleOutput){
return COStreamJS.parser.parse(`
composite conv2DLayer_${layer.level}(input stream<double x>In, output stream<double x>Out){
int i;
Out = splitjoin(In){
split duplicate();
for(i = 0; i < ${layer.filters} ;i++){
add ${conv2D_comp.compName}(i);
}
join roundrobin();
};
}
`)[0]
}
return COStreamJS.parser.parse(`
composite conv2DLayer_${layer.level}(input stream<double x>In, output stream<double x>Out0, stream<double x>Out1){
stream<double x> MID;
int i;
MID = splitjoin(In){
split duplicate();
for(i = 0; i < ${layer.filters} ;i++){
add ${conv2D_comp.compName}(i);
}
join roundrobin();
};
(Out0, Out1) = _copy(MID){
work{
Out0[0].x = MID[0].x;
Out1[0].x = MID[0].x;
}
window{
MID sliding(1,1);
Out0 tumbling(1);
Out1 tumbling(1);
}
};
}
`)[0]
}
function MakeConv2DKernel(/** @type {conv2DLayerNode} */ layer){
const { level, strides } = layer
const [inputSize0,inputSize1,depth] = layer.inputSize // inputSize0 用不到但不要删除
const inputWindowSize = layer.inputSize.reduce((a,b)=>a*b)
const [rows,cols] = layer.kernel_size
const [m,n] = layer.outputFeatureMapSize
return COStreamJS.parser.parse(`
composite conv2DKernel_${level}(input stream<double x>In, output stream<double x>Out){
param
int kernelIndex;
Out = conv2D_${level}(In){
init {
int j,n,m;
for(j=0;j<${depth};j++){
for(n=0;n<${rows};n++){
for(m=0;m<${cols};m++){
_weight_${level}[kernelIndex][j][n][m]= random() - 0.5;
}
}
}
}
work {
int i, j, n, m, d, pushIndex = 0;
double temp;
for (m = 0; m < ${m}; m++){
for (n = 0; n < ${n}; n++){
temp = 0;
for (d = 0; d < ${depth}; d++){
for (i = 0; i < ${rows}; i++){
for (j = 0; j < ${cols}; j++){
// 取一个 三维 [inputSize0][inputSize1][depth] 向量 的 in[m*strides0+i][n*strides1+j][d] 的线性下标
int index = d + (n * ${strides[1]} + j) * ${depth} + (m * ${strides[0]} + i) * ${inputSize1} * ${depth} ;
temp += In[index].x * _weight_${level}[kernelIndex][d][i][j];
}
}
}
Out[pushIndex].x = temp;
pushIndex++;
}
}
}
window {
In sliding(${inputWindowSize}, ${inputWindowSize});
Out tumbling(${m*n});
}
};
}
`)[0]
}
function MakeLossComposite(/** @type {layerNode} */layer) {
let win = 0
if (layer instanceof denseLayerNode) {
win = layer.cols
}else if(layer instanceof activationLayerNode){
win = layer.count
} else {
error("未支持的 layer 类型")
}
var compStr = `composite loss(input stream<double x>In0, stream<double x>In1, output stream<double x>Out) {
Out = loss(In0,In1){
init{}
work{
int i;
for(i=0;i<${win};i++){
Out[i].x = In0[i].x - In1[i].x;
}
}
window{
In0 sliding(${win},${win});
In1 sliding(${win},${win});
Out tumbling(${win},${win});
}
};
}`
const comp = COStreamJS.parser.parse(compStr)[0]
// 加入符号表
COStreamJS.S.compTable[comp.compName] = { composite: comp }
COStreamJS.ast.push(comp)
return comp
}
function makeMaxPooling2DLayer(/** @type {maxPooling2DLayerNode} */layer, singleOutput = false){
const comp = makeMaxPooling2DKernel(layer)
COStreamJS.S.compTable[comp.compName] = { composite: comp }
COStreamJS.ast.push(comp)
if(singleOutput){
return COStreamJS.parser.parse(`
composite maxPooling2DLayer_${layer.level}(input stream<double x>In, output stream<double x>Out){
int i;
Out = splitjoin(In){
split roundrobin();
for(i = 0; i < ${layer.depth} ;i++){
add ${comp.compName}(i);
}
join roundrobin();
};
}
`)[0]
}
return COStreamJS.parser.parse(`
composite maxPooling2DLayer_${layer.level}(input stream<double x>In, output stream<double x>Out0, stream<double x>Out1){
stream<double x> MID;
int i;
MID = splitjoin(In){
split roundrobin();
for(i = 0; i < ${layer.depth} ;i++){
add ${comp.compName}();
}
join roundrobin();
};
(Out0, Out1) = _copy(MID){
work{
Out0[0].x = MID[0].x;
Out1[0].x = MID[0].x;
}
window{
MID sliding(1,1);
Out0 tumbling(1);
Out1 tumbling(1);
}
};
}
`)[0]
}
function makeMaxPooling2DKernel(/** @type {maxPooling2DLayerNode} */layer){
const { level } = layer
const [output0,output1] = layer.outputPooledSize
const size = layer.pool_size
const inputWindowSize = layer.inputSize[0] * layer.inputSize[1]
const [_,inputSize1] = layer.inputSize
return COStreamJS.parser.parse(`
composite maxPooling2DKernel_${level}(input stream<double x>In, output stream<double x>Out){
Out = maxPooling2D_${level}(In){
init {}
work {
int i, j, n, m;
double max;
for (m = 0; m < ${output0}; m++){
for (n = 0; n < ${output1}; n++){
i = 0;
j = 0;
max = In[(m * ${size} + i) * ${inputSize1} + n * ${size} + j].x;
for (i = 0; i < ${size}; i++){
for (j = 0; j < ${size}; j++){
if (max < In[(m * ${size} + i) * ${inputSize1} + n * ${size} + j].x){
max = In[(m * ${size} + i) * ${inputSize1} + n * ${size} + j].x;
}
}
}
Out[m*${output1} +n].x = max;
}
}
}
window {
In sliding(${inputWindowSize}, ${inputWindowSize});
Out tumbling(${output0*output1});
}
};
}
`)[0]
}
function makeAveragePooling2DLayer(/** @type {averagePooling2DLayerNode} */layer, singleOutput = false){
}
function makeActivationLayer(/** @type {activationLayerNode} */layer){
const { level, count } = layer
const funcName = layer.arg_list[0].source.slice(1,-1) // 刚拿到是 "relu", 通过 slice 移出左右两侧双引号
if(!["relu", "softmax","sigmoid"].includes(funcName)){
error(layer._loc, `不支持此种激活函数:${funcName}, 仅支持 relu,softmax,sigmoid`)
}
const works = {
"relu": `for (i = 0; i < ${count}; i++){
if (In[i].x > 0){
out0[i].x = In[i].x;
out1[i].x = In[i].x;
derivative[i].x = 1;
}
else{
out0[i].x = 0;
out1[i].x = 0;
derivative[i].x = 0;
}
}`,
"softmax": `double total = 0, res;
for (i = 0; i < ${count}; i++){
total += exp(In[i].x);
}
for (i = 0; i < ${count}; i++){
res = exp(In[i].x) / total;
out0[i].x = res;
out1[i].x = res;
derivative[i].x = res;
}`,
"sigmoid": `double res;
for (i = 0; i < ${count}; i++) {
res = 1 / ( 1 + exp(-In[i].x));
out0[i].x = res;
out1[i].x = res;
derivative[i].x = res * (1 - res);
}
`
}
if (!layer.nextLayer || layer.nextLayer instanceof averagePooling2DLayerNode) {
var compStr = `composite Activation_${level}(input stream<double x>In, output stream<double x>out0, stream<double x>derivative) {
(out0,derivative) = activation_${funcName}_${level}(In){
init{}
work{
int i;
${works[funcName].split('\n').filter(str => !(/out1/.test(str))).join('\n')}
}
window{
In sliding(${count},${count});
out0 tumbling(${count},${count});
derivative tumbling(${count},${count});
}
};
}`
} else {
var compStr = `composite Activation_${level}(input stream<double x>In, output stream<double x>out0,stream<double x>out1, stream<double x>derivative) {
(out0,out1,derivative) = activation_${funcName}_${level}(In){
init{}
work{
int i;
${works[funcName]}
}
window{
In sliding(${count},${count});
out0 tumbling(${count},${count});
out1 tumbling(${count},${count});
derivative tumbling(${count},${count});
}
};
}`
}
return COStreamJS.parser.parse(compStr)[0]
}
function MakeBackComposite(layer) {
if (layer instanceof denseLayerNode) {
var comp = MakeDDenseComposite(layer)
}else if(layer instanceof conv2DLayerNode){
var comp = MakeDConv2DComposite(layer)
}else if(layer instanceof maxPooling2DLayerNode){
var comp = makeDMaxPooling2DLayer(layer)
}else if(layer instanceof activationLayerNode){
var comp = makeDActivitionComposite(layer)
}
// 加入符号表
COStreamJS.S.compTable[comp.compName] = { composite: comp }
COStreamJS.ast.push(comp)
return comp
}
function MakeDDenseComposite(/** @type {denseLayerNode} */layer) {
const { level, rows, cols } = layer
var compStr = `composite dDense_${level}(input stream<double x>In0,stream<double x>In1, output stream<double x>Out) {
Out = dDense${level}(In0,In1){
init{}
work{
int i,j;
double temp = 0;
for (i = 0; i < ${rows}; i++)
{
temp = 0;
for (j = 0; j < ${cols}; j++)
{
temp += In0[j].x * _weight_${level}[i][j];
}
Out[i].x = temp;
}
double lr = 0.100000;
for (i = 0; i < ${rows}; i++)
{
for (j = 0; j < ${cols}; j++)
{
_weight_${level}[i][j] = _weight_${level}[i][j] - In0[j].x * In1[i].x * lr;
}
}
}
window{
In0 sliding(${cols},${cols});
In1 sliding(${rows},${rows});
Out tumbling(${rows},${rows});
}
};
}`
return COStreamJS.parser.parse(compStr)[0]
}
// 生成名为"dConv2DLayer_" + level 的卷积层反向传播计算节点
function MakeDConv2DComposite(/** @type {conv2DLayerNode} */ layer){
const { level } = layer
const comp = COStreamJS.parser.parse(`
composite dConv2DLayer_${level}(input stream<double x>In0,stream<double x>In1, output stream<double x>Out) {
;
}
`)[0]
comp.body.stmt_list = MakeDConv2DLayerBodyStmt(layer, comp)
return comp
}
/** @returns {binopNode} */
function operToBinop(/** @type {operatorNode} */oper){
return new binopNode(null, new parenNode(null, oper.outputs), '=', oper)
}
function MakeDConv2DLayerBodyStmt(/** @type {conv2DLayerNode} */ layer, /** @type {compositeNode} */comp){
const compStmtList = [] // 要返回的 body_stmt
let streamName = "DConv2dStream_" + layer.level;
// join operator的输入流
let inputs_join = [];
// list<compositeCallNode *> *comCallList = new list<compositeCallNode *>();
const strType = comp.inout.input_list[0].strType
const streamDecl = new declareNode(null, strType, []);
// 数据流声明 stream<double x> dilateAndExtend_2;
const dilateAndExtendStream = "dilateAndExtend_" + layer.level
streamDecl.init_declarator_list.push(dilateAndExtendStream)
compStmtList.push(streamDecl);
// 构建 Dilate_Extend
compStmtList.push(makeConv2DDilateAndExtendOperator(layer, ["In0"], [dilateAndExtendStream]));
let dupCount = layer.inputSize[layer.inputSize.length - 1];
// splitOperator1 将误差duplicate成filters份, splitOperator2 将传入正向传播的输入再次传入到反向传播中,并duplicate成多份
const splitOperator1 = makeSpecialSplitOperator(dilateAndExtendStream, dupCount, layer.level);
const splitOperator2 = makeSpecialSplitOperator('In1', dupCount, layer.level);
compStmtList.push(operToBinop(splitOperator1));
compStmtList.push(operToBinop(splitOperator2));
// 加入数据流声明中
debugger;
[...splitOperator1.outputs, ...splitOperator2.outputs].forEach(name => streamDecl.init_declarator_list.push(name))
const dKernelComp = makeDConv2DKernel(layer);
//开始连接 oper
for(let i=0; i< dupCount; i++){
const tempName = streamName + "_" + i;
streamDecl.init_declarator_list.push(tempName)
//compositeCall的输出流是join节点的输入流
inputs_join.push(tempName);
// kernel的输出流
const call_outputs = [tempName];
//compositeCall的输入流
const call_inputs = [splitOperator1.outputs[i], splitOperator2.outputs[i]]
// compositeCallNode *call = new compositeCallNode(call_outputs, tempName, argList, call_inputs, dKernelComp);
const call = new compositeCallNode(null,dKernelComp.compName, call_inputs, [new constantNode(null,i)]);
call.outputs = call_outputs
compStmtList.push(call);
}
const joinOperator = makeSpecialJoinOperator('Out', inputs_join, layer.level);
compStmtList.push(operToBinop(joinOperator));
return compStmtList;
}
function makeConv2DDilateAndExtendOperator(/** @type {conv2DLayerNode} */ layer, inputs_id, outputs_id){
const level = layer.level
const [stride0, stride1] = layer.strides
const [kernel0, kernel1] = layer.kernel_size
const [inputErrorSize0,inputErrorSize1] = layer.inputErrorSize
const filters = layer.filters
const [outputFeatureMapSize0,outputFeatureMapSize1] = layer.outputFeatureMapSize
const slidingWindowSize = outputFeatureMapSize0 * outputFeatureMapSize1 * filters
const tumblingWindowSize = inputErrorSize0 * inputErrorSize1 * filters
return COStreamJS.parser.parse(`
composite conv2D_Dilate_Extend_${level}(){
${outputs_id} = conv2D_Dilate_Extend_${level}(${inputs_id}){
init{}
work{
int i, j, filters;
for (i=0;i<${tumblingWindowSize};i++){
dilateAndExtend_${level}[i].x = 0;
}
for (i = 0; i < ${outputFeatureMapSize0}; i++){
for (j = 0; j < ${outputFeatureMapSize1}; j++){
for (filters = 0; filters < ${filters}; filters++){
// [i][j][filters] => [kernel0 + i * stride0][kernel1 + j * stride1][filters];
int dilate_index = (${stride0} * i + ${kernel0}) * ${inputErrorSize1*filters} + (${stride1} * j + ${kernel1}) * ${filters} + filters;
int in_index = i * ${outputFeatureMapSize1*filters} + j * ${filters} + filters;
dilateAndExtend_${level}[dilate_index].x = ${inputs_id}[in_index].x;
}
}
}
}
window{
${inputs_id} sliding(${slidingWindowSize},${slidingWindowSize});
${outputs_id} tumbling(${tumblingWindowSize});
}
};
}
`)[0].body.stmt_list[0]
}
function makeDConv2DKernel(/** @type {conv2DLayerNode} */ layer){
const { level, filters } = layer
const [inputSize0,inputSize1, depth] = layer.inputSize
const [kernel0, kernel1] = layer.kernel_size
const [inputErrorSize0,inputErrorSize1] = layer.inputErrorSize
const [stride0, stride1] = layer.strides
const slidingWindowSize = inputErrorSize0 * inputErrorSize1 * filters
const in1_WindowSize = inputSize0 * inputSize1 * depth
const comp = COStreamJS.parser.parse(`
composite dConv2D_${level}(input stream<double x>in0, stream<double x>in1, output stream<double x>out){
param
int depthIndex;
out = dConv2D_${level}(in0,in1){
init{}
work{
int i, j, n, m, filterIndex;
double temp;
for (m = 0; m < ${inputSize0}; m++){
for (n = 0; n < ${inputSize1}; n++){
temp = 0;
for (filterIndex = 0; filterIndex < ${filters}; filterIndex++){
for (i = 0; i < ${kernel0}; i++){
for (j = 0; j < ${kernel1}; j++){
temp += in0[(m + i) * ${inputErrorSize1} * ${filters} + (n + j) * ${filters} + filterIndex].x * _weight_${level}[filterIndex][depthIndex][${kernel0-1} - i][${kernel1-1} - j];
}
}
}
out[m * ${inputSize1} + n].x = temp;
}
}
for (filterIndex = 0; filterIndex < ${filters}; filterIndex++){
for (i = 0; i < ${kernel0}; i++){
for (j = 0; j < ${kernel1}; j++){
temp = 0;
for (m = 0; m < ${inputSize0}; m++){
for (n = 0; n < ${inputSize1}; n++){
int in0_index = ( ${kernel0} - 1 + m * ${stride0} ) * ${inputErrorSize1*filters} + (${kernel1} -1 + n * ${stride1}) * ${filters} + filterIndex;
int in1_index = ( i + m * ${stride0} ) * ${inputSize1*depth} + ( j + n*${stride1} )*${depth} + depthIndex;
temp += in0[in0_index].x * in1[in1_index].x;
}
}
_weight_${level}[filterIndex][depthIndex][i][j] -= temp;
}
}
}
}
window{
in0 sliding(${slidingWindowSize},${slidingWindowSize});
in1 sliding(${in1_WindowSize},${in1_WindowSize});
out tumbling(${inputSize0 * inputSize1});
}
};
}
`)[0]
COStreamJS.S.compTable[comp.compName] = { composite: comp };
COStreamJS.ast.push(comp);
return comp;
}
function makeSpecialSplitOperator(inputStreamName, splitCount, level, isRoundrobin = undefined){
const outputs = Array.from({length: splitCount}).map((_,idx)=> inputStreamName+'_'+idx);
if(isRoundrobin){
return COStreamJS.parser.parse(`
composite special_roundrobin(input stream<double x>${inputStreamName}){
(${outputs.join(',')}) = special_roundrobin_${level}(${inputStreamName}){
init{}
work{
${outputs.map((name,idx) => `${name}[0] = ${inputStreamName}[${idx}];`).join('\n')}
}
window{
${inputStreamName} sliding(${splitCount},${splitCount});
${outputs.map(name => name + ' tumbling(1);').join('\n')}
}
};
}
`)[0].body.stmt_list[0].right
}
return COStreamJS.parser.parse(`
composite special_duplicate(input stream<double x>${inputStreamName}){
(${outputs.join(',')}) = special_duplicate_${level}(${inputStreamName}){
init{}
work{
${outputs.map(name => name + '[0]=' + inputStreamName + '[0];').join('\n')}
}
window{
${inputStreamName} sliding(1,1);
${outputs.map(name => name + ' tumbling(1);').join('\n')}
}
};
}
`)[0].body.stmt_list[0].right
}
function makeSpecialJoinOperator(outputStreamName, /** @type {string[]} */inputs, level){
return COStreamJS.parser.parse(`
composite special_join(output stream<double x>${outputStreamName}){
${outputStreamName} = special_join_${level}(${inputs.join(',')}){
init{}
work{
int i=0;
${inputs.map(name => outputStreamName +'[i++] = ' + name + '[0];').join('\n')}
}
window{
${inputs.map(name => name + ' sliding(1,1);').join('\n')}
${outputStreamName} tumbling(${inputs.length});
}
};
}
`)[0].body.stmt_list[0].right
}
function makeDMaxPooling2DLayer(/** @type {maxPooling2DLayerNode} */layer){
const { level } = layer
const comp = COStreamJS.parser.parse(`
composite dMaxPooling2DLayer_${level}(input stream<double x>In0,stream<double x>In1, output stream<double x>Out) {
;
}
`)[0]
comp.body.stmt_list = makeDMaxPooling2DBodyStmt(layer, comp)
return comp
}
function makeDMaxPooling2DBodyStmt(/** @type {maxPooling2DLayerNode} */layer, comp){
const compStmtList = [] // 要返回的 body_stmt
let streamName = "DMaxPooling2D_Stream_" + layer.level;
// join operator的输入流
let inputs_join = [];
const strType = comp.inout.input_list[0].strType
const streamDecl = new declareNode(null, strType, []);
compStmtList.push(streamDecl);
let dupCount = layer.inputSize[layer.inputSize.length - 1];
// splitOperator1 将误差roundrobin成filters份, splitOperator2 将传入正向传播的输入再次传入到反向传播中,并roundrobin成多份
const splitOperator1 = makeSpecialSplitOperator("In0", dupCount, layer.level,1);
const splitOperator2 = makeSpecialSplitOperator('In1', dupCount, layer.level,1);
compStmtList.push(operToBinop(splitOperator1));
compStmtList.push(operToBinop(splitOperator2));
// 加入数据流声明中
debugger;
[...splitOperator1.outputs, ...splitOperator2.outputs].forEach(name => streamDecl.init_declarator_list.push(name))
const dKernelComp = makeDMaxPooling2DKernel(layer);
//开始连接 oper
for(let i=0; i< dupCount; i++){
const tempName = streamName + "_" + i;
streamDecl.init_declarator_list.push(tempName)
//compositeCall的输出流是join节点的输入流
inputs_join.push(tempName);
// kernel的输出流
const call_outputs = [tempName];
//compositeCall的输入流
const call_inputs = [splitOperator1.outputs[i], splitOperator2.outputs[i]]
// compositeCallNode *call = new compositeCallNode(call_outputs, tempName, argList, call_inputs, dKernelComp);
const call = new compositeCallNode(null,dKernelComp.compName, call_inputs);
call.outputs = call_outputs
compStmtList.push(call);
}
const joinOperator = makeSpecialJoinOperator('Out', inputs_join, layer.level);
compStmtList.push(operToBinop(joinOperator));
return compStmtList;
}
function makeDMaxPooling2DKernel(/** @type {maxPooling2DLayerNode} */layer){
const { level } = layer
const [error0,error1] = layer.outputPooledSize
const [inputSize0, inputSize1] = layer.inputSize
const size = layer.pool_size
const comp = COStreamJS.parser.parse(`
composite dMaxPooling2DKernel_${level}(input stream<double x>in0, stream<double x>in1, output stream<double x>out){
out = dMaxPooling2DKernel_${level}(in0,in1){
init{}
work{
int i, j, n, m;
double max;
for (m = 0; m < ${error0}; m++){
for (n = 0; n < ${error1}; n++){
i = 0;
j = 0;
max = in1[(m * ${size} + i) * ${inputSize1} + n * ${size} + j].x;
for (i = 0; i < ${size}; i++){
for (j = 0; j < ${size}; j++){
if (max < in1[(m * ${size} + i) * ${inputSize1} + n * ${size} + j].x){
max = in1[(m * ${size} + i) * ${inputSize1} + n * ${size} + j].x;
}
}
}
for (i = 0; i < ${size}; i++){
for (j = 0; j < ${size}; j++){
if (max == in1[(m * ${size} + i) * ${inputSize1} + n * ${size} + j].x){
out[(m * ${size} + i) * ${inputSize1} + n * ${size} + j].x = in0[m * ${error1} + n].x;
}
}
}
}
}
}
window{
in0 sliding(${error0 * error1},${error0 * error1});
in1 sliding(${inputSize0 * inputSize1},${inputSize0 * inputSize1});
out tumbling(${inputSize0 * inputSize1});
}
};
}
`)[0]
COStreamJS.S.compTable[comp.compName] = { composite: comp };
COStreamJS.ast.push(comp);
return comp;
}
function makeDActivitionComposite(/** @type {activationLayerNode} */layer){
const { level, count } = layer
const funcName = layer.arg_list[0].source.slice(1,-1) // 刚拿到是 "relu", 通过 slice 移出左右两侧双引号
if(!["relu", "softmax","sigmoid"].includes(funcName)){
error(layer._loc, `不支持此种激活函数:${funcName}, 仅支持 relu,softmax,sigmoid`)
}
const works = {
"relu": `for (i = 0; i < ${count}; i++) {
out[i].x = error[i].x * In[i].x;
}`,
"softmax": `int j;
for(i = 0; i < ${count}; i++) {
double temp = 0;
for (j = 0; j < ${count}; j++) {
if (i == j) {
temp += error[j].x * In[i].x * (1 - In[i].x);
} else {
temp += error[j].x * In[i].x * In[j].x;
}
}
out[i].x = temp;
}`,
"sigmoid": `for (i = 0; i < ${count}; i++) {
out[i].x = error[i].x * In[i].x;
}`,
}
var compStr = `composite DActivation_${level}(input stream<double x>error, stream<double x>In, output stream<double x>out) {
out = dActivation_${funcName}_${level}(error, In){
init{}
work{
int i;
${works[funcName]}
}
window{
In sliding(${count},${count});
error sliding(${count},${count});
out tumbling(${count},${count});
}
};
}`
return COStreamJS.parser.parse(compStr)[0]
}