Imagine a framework-agnostic DSL with strong typing, dimension and ownership checking and lots of syntax sugar. What would it be like? As interesting as it is, here is why there needs to a langauge for neural network:
Multi-target. Write once, run everywhere(interpreted in PyTorch or compiled to Tensorflow MetaGraphDef).
Typechecking with good type annotation.
Parallellize with language-level directives
Composition(constructing larger systems with building blocks) is easier
Ownership system because GC in CUDA is a nightmare
No more frustration from deciphering undocumented code written by researchers. The issue is, an overwhelming majority of researchers are not programmers, who care about the aesthetics of clean code and helpful documentation. I get it - people are lazy and unsafe unless the compiler forces them to annotate their code.
About syntax:
Stateful symbols are capitalized
Impure functions must be annotated
Local variables cannot be shadowed
Function args have keyword
use conv::{Conv2d, Dropout2d, maxpool2d};
use nonlin::relu;
use lin::Linear;
// three levels of abstraction: node, weights, graph
// ? stands for batch size, channel, height, width
// outputs tensor of shape [batch_size, 10]
node Mnist<?,c,h,w -> ?,OUT> {
// define symbol level macro for both type/dimension and scope variable
FC1 = 320;
FC2 = 50;
OUT = 10;
}
weights Mnist<?,c,h,w -> ?,OUT> {
conv1 = Conv2d::<?,c,h,w -> ?,c,h,w>::new(in_ch=1, out_ch=10, kernel_size=5);
conv2 = Conv2d::<?,c,h,w -> ?,c,h,w>::new(in_ch=10, out_ch=20, kernel_size=5);
dropout = Dropout2d::<?,c,h,w -> ?,c,h,w>::new(p=0.5);
fc1 = Linear::<?,FC1 -> ?,FC2>::new();
fc2 = Linear::<?,FC2 -> ?,OUT>::new();
}
graph Mnist<?,c,h,w -> ?,OUT> {
fn new() {
fc1.init_normal(std=1.);
fc2.init_normal(std=1.);
}
fn forward(self, x) {
x
|> conv1 |> maxpool2d(kernel_size=2)
|> conv2 |> dropout |> maxpool2d(kernel_size=2)
|> view(?, FC1)
|> fc1 |> relu
|> self.fc2()
|> log_softmax(dim=1)
}
fn fc2(self, x: <?,FC2>) -> <?,OUT> {
x |> fc2 |> relu
}
}
compiles to
from torch import nn, F
class Mnist(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, 10, kernel_size=5)
self.conv2 = nn.Conv2d(10, 20, kernel_size=5)
self.conv2_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 50)
self.fc2 = nn.Linear(50, 10)
def forward(self, x):
x = F.relu(F.max_pool2d(self.conv1(x), 2))
x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = F.relu(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x, dim=1)
Log in or sign up for Devpost to join the conversation.