Commit a5e00e92 authored by Thomas Michael Timmermanns's avatar Thomas Michael Timmermanns Committed by Thomas Michael Timmermanns

New version for direct embedding.

Added CNNArchCompilationUnitSymbol, changed ArchitectureSymbol and changed Cocos to work with adapter.
Removed data format declaration from input and output.
parent ad995430
......@@ -25,26 +25,26 @@ There are more compact versions of the same architecture but we will get to that
All predefined methods are listed at the end of this document.
```
architecture Alexnet_alt(img_height=224, img_width=224, img_channels=3, classes=10){
def input Z(0:255)^{H:img_height, W:img_width, C:img_channels} image
def output Q(0:1)^{C:classes} predictions
def input Z(0:255)^{img_channels, img_height, img_width} image
def output Q(0:1)^{classes} predictions
image ->
Convolution(kernel=(11,11), channels=96, stride=(4,4), padding="no_loss") ->
Lrn(nsize=5, alpha=0.0001, beta=0.75) ->
Pooling(type="max", kernel=(3,3), stride=(2,2), padding="no_loss") ->
Pooling(pool_type="max", kernel=(3,3), stride=(2,2), padding="no_loss") ->
Relu() ->
Split(n=2) ->
(
[0] ->
Convolution(kernel=(5,5), channels=128) ->
Lrn(nsize=5, alpha=0.0001, beta=0.75) ->
Pooling(type="max", kernel=(3,3), stride=(2,2), padding="no_loss") ->
Pooling(pool_type="max", kernel=(3,3), stride=(2,2), padding="no_loss") ->
Relu()
|
[1] ->
Convolution(kernel=(5,5), channels=128) ->
Lrn(nsize=5, alpha=0.0001, beta=0.75) ->
Pooling(type="max", kernel=(3,3), stride=(2,2), padding="no_loss") ->
Pooling(pool_type="max", kernel=(3,3), stride=(2,2), padding="no_loss") ->
Relu()
) ->
Concatenate() ->
......@@ -56,14 +56,14 @@ architecture Alexnet_alt(img_height=224, img_width=224, img_channels=3, classes=
Convolution(kernel=(3,3), channels=192) ->
Relu() ->
Convolution(kernel=(3,3), channels=128) ->
Pooling(type="max", kernel=(3,3), stride=(2,2), padding="no_loss") ->
Pooling(pool_type="max", kernel=(3,3), stride=(2,2), padding="no_loss") ->
Relu()
|
[1] ->
Convolution(kernel=(3,3), channels=192) ->
Relu() ->
Convolution(kernel=(3,3), channels=128) ->
Pooling(type="max", kernel=(3,3), stride=(2,2), padding="no_loss") ->
Pooling(pool_type="max", kernel=(3,3), stride=(2,2), padding="no_loss") ->
Relu()
) ->
Concatenate() ->
......@@ -100,18 +100,17 @@ An architecture in CNNArch can have multiple inputs and outputs.
Multiple inputs (or outputs) of the same form can be combined to an array.
Assuming `h` and `w` are architecture parameter, the following is a valid example:
```
def input Z(0:255)^{H:h, W:w, C:3} image[2]
def input Q(-oo:+oo)^{C:10} additionalData
def output Q(0:1)^{C:3} predictions
def input Z(0:255)^{3, h, w} image[2]
def input Q(-oo:+oo)^{10} additionalData
def output Q(0:1)^{3} predictions
```
The first line defines the input *image* as an array of two rgb (or bgr) images with a resolution of `h` x `w`.
The part `Z(0:255)`, which corresponds to the type definition in EmbeddedMontiArc, restricts the values to integers between 0 and 255.
The following line `{H:h, W:w, C:3}` declares the shape of the input.
The shape denotes the dimensionality in form of height `H`, width `W` and depth `C`(number of channels).
The following line `{3, h, w}` declares the shape of the input.
The shape denotes the dimensionality in form of depth (number of channels), height and width.
Here, the height is initialized as `h`, the width as `w` and the number of channels is 3.
The second line defines another input with one dimension of size 10 and arbitrary rational values.
The last line defines an one-dimensional output of size 3 with rational values between 0 and 1 (probabilities of 3 classes).
The order of `H`, `W` and `C` determines the shape of the input or output of the network.
If an input or output is an array, it can be used in the architecture in two different ways.
Either a single element is accessed or the array is used as a whole.
......@@ -198,13 +197,13 @@ The comparison operators do not work reliably for the comparison of tuple (they
This version of Alexnet, which uses method construction, argument sequences and special arguments, is identical to the one in the section Basic Structure.
```
architecture Alexnet_alt2(img_height=224, img_width=224, img_channels=3, classes=10){
def input Z(0:255)^{H:img_height, W:img_width, C:img_channels} image
def output Q(0:1)^{C:classes} predictions
def input Z(0:255)^{img_channels, img_height, img_width} image
def output Q(0:1)^{classes} predictions
def conv(filter, channels, convStride=1, poolStride=1, hasLrn=false, convPadding="same"){
Convolution(kernel=(filter,filter), channels=channels, stride=(convStride,convStride), padding=convPadding) ->
Lrn(nsize=5, alpha=0.0001, beta=0.75, ?=hasLrn) ->
Pooling(type="max", kernel=(3,3), stride=(poolStride,poolStride), padding="no_loss", ?=(poolStride != 1)) ->
Pooling(pool_type="max", kernel=(3,3), stride=(poolStride,poolStride), padding="no_loss", ?=(poolStride != 1)) ->
Relu()
}
def split1(i){
......@@ -287,20 +286,20 @@ All predefined methods start with a capital letter and all constructed methods h
* **p** (1 >= float >= 0, optional, default=0.5): Fraction of the input that gets dropped out during training time.
* **Pooling(type, kernel, stride=(1,1), padding="same")**
* **Pooling(pool_type, kernel, stride=(1,1), padding="same")**
Performs pooling on the input.
* **type** ({"avg", "max"}, required): Pooling type to be applied.
* **pool_type** ({"avg", "max"}, required): Pooling type to be applied.
* **kernel** (integer tuple > 0, required): convolution kernel size: (height, width).
* **stride** (integer tuple > 0, optional, default=(1,1)): convolution stride: (height, width).
* **padding** ({"valid", "same", "no_loss"}, optional, default="same"): One of "valid", "same" or "no_loss". "valid" means no padding. "same" results in padding the input such that the output has the same length as the original input divided by the stride (rounded up). "no_loss" results in minimal padding such that each input is used by at least one filter (identical to "valid" if *stride* equals 1).
* **GlobalPooling(type)**
* **GlobalPooling(pool_type)**
Performs global pooling on the input.
* **type** ({"avg", "max"}, required): Pooling type to be applied.
* **pool_type** ({"avg", "max"}, required): Pooling type to be applied.
* **Lrn(nsize, knorm=2, alpha=0.0001, beta=0.75)**
......
......@@ -30,7 +30,7 @@
<groupId>de.monticore.lang.monticar</groupId>
<artifactId>cnn-arch</artifactId>
<version>0.1.1-SNAPSHOT</version>
<version>0.2.0-SNAPSHOT</version>
<!-- == PROJECT DEPENDENCIES ============================================= -->
......@@ -42,7 +42,7 @@
<mc.grammars.assembly.version>0.0.6</mc.grammars.assembly.version>
<SIUnit.version>0.0.10-SNAPSHOT</SIUnit.version>
<Common-MontiCar.version>0.0.10-SNAPSHOT</Common-MontiCar.version>
<Math.version>0.0.10-SNAPSHOT</Math.version>
<Math.version>0.0.11-SNAPSHOT</Math.version>
<!-- .. Libraries .................................................. -->
<guava.version>18.0</guava.version>
<junit.version>4.12</junit.version>
......
......@@ -2,39 +2,36 @@ package de.monticore.lang.monticar;
grammar CNNArch extends de.monticore.lang.math.Math {
CNNArchCompilationUnit = Architecture;
symbol scope CNNArchCompilationUnit = "architecture"
name:Name&
( "(" (ArchitectureParameter || ",")* ")" )? "{"
ioDeclarations:IODeclaration*
Architecture
"}";
symbol scope Architecture = "architecture"
name:Name&
( "(" (ArchitectureParameter || ",")* ")" )? "{"
declarations:ArchDeclaration*
body:ArchBody "}";
Architecture = methodDeclaration:MethodDeclaration*
body:ArchBody;
interface ArchDeclaration;
interface ArchitectureElement;
interface Variable;
ast Variable = method String getName(){};
IODeclaration implements ArchDeclaration = "def"
(in:"input" | out:"output")
type:ArchType
Name&
(ArrayDeclaration)?;
IODeclaration = "def"
(in:"input" | out:"output")
type:ArchType
Name&
(ArrayDeclaration)?;
ArchType implements Type = ElementType "^" Shape;
ArchType = ElementType "^" Shape;
Shape = "{" dimensions:(DimensionArgument || ",")* "}";
DimensionArgument = (name:"H" ":" height:ArchSimpleExpression
| name:"W" ":" width:ArchSimpleExpression
| name:"C" ":" channels:ArchSimpleExpression);
Shape = "{" dimensions:(ArchSimpleExpression || ",")* "}";
ArchitectureParameter implements Variable = Name& ("=" default:ArchSimpleExpression)?;
MethodDeclaration implements ArchDeclaration = "def"
Name& "("
parameters:(MethodParameter || ",")* ")" "{"
body:ArchBody "}";
MethodDeclaration = "def"
Name& "("
parameters:(MethodParameter || ",")* ")" "{"
body:ArchBody "}";
MethodParameter implements Variable = Name& ("=" default:ArchSimpleExpression)?;
......@@ -61,7 +58,7 @@ grammar CNNArch extends de.monticore.lang.math.Math {
ArchExpression = (expression:ArchSimpleExpression | sequence:ArchValueSequence);
interface ArchValueSequence;
interface ArchValueSequence;
ArchParallelSequence implements ArchValueSequence = "[" parallelValues:(ArchSerialSequence || "|")* "]";
......
......@@ -40,9 +40,10 @@ public class CNNArchCocos {
public static CNNArchCoCoChecker createPostResolveChecker() {
return new CNNArchCoCoChecker()
.addCoCo(new CheckIOType())
.addCoCo(new CheckLayerInputs())
.addCoCo(new CheckIOAccessAndIOMissing())
.addCoCo(new CheckIOShape())
.addCoCo(new CheckUnusedASTIODeclaration())
.addCoCo(new CheckArchitectureFinished());
}
......@@ -53,7 +54,6 @@ public class CNNArchCocos {
.addCoCo(new CheckMethodLayer())
.addCoCo(new CheckRangeOperators())
.addCoCo(new CheckVariableName())
.addCoCo(new CheckUnknownIO())
.addCoCo(new CheckArgument())
.addCoCo(new CheckMethodName())
.addCoCo(new CheckMethodRecursion());
......
......@@ -39,16 +39,6 @@ public class CheckArgument implements CNNArchASTArchArgumentCoCo {
"Possible arguments are: " + Joiners.COMMA.join(method.getParameters())
, node.get_SourcePositionStart());
}
else {
/*if (argument.getRhs().getValue().isPresent()){
argument.checkConstraints();
}*/
/*if (argument.getRhs().isResolvable()) {
argument.getRhs().resolveOrError();
argument.checkConstraints();
argument.getRhs().reset();
}*/
}
}
}
......@@ -20,7 +20,9 @@
*/
package de.monticore.lang.monticar.cnnarch._cocos;
import de.monticore.lang.monticar.cnnarch._ast.ASTArchitecture;
import de.monticore.lang.monticar.cnnarch._ast.ASTIODeclaration;
import de.monticore.lang.monticar.cnnarch._symboltable.ArchitectureSymbol;
import de.monticore.lang.monticar.cnnarch._symboltable.IODeclarationSymbol;
import de.monticore.lang.monticar.cnnarch._symboltable.IOLayerSymbol;
import de.monticore.lang.monticar.cnnarch.helper.ErrorCodes;
......@@ -33,17 +35,19 @@ import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class CheckIOAccessAndIOMissing implements CNNArchASTIODeclarationCoCo {
public class CheckIOAccessAndIOMissing implements CNNArchASTArchitectureCoCo {
@Override
public void check(ASTIODeclaration node) {
IODeclarationSymbol ioDeclaration = (IODeclarationSymbol) node.getSymbol().get();
public void check(ASTArchitecture node) {
ArchitectureSymbol architecture = (ArchitectureSymbol) node.getSymbol().get();
if (ioDeclaration.getArrayLength() == 1){
checkSingleIO(ioDeclaration);
}
else {
checkIOArray(ioDeclaration);
for (IODeclarationSymbol ioDeclaration : architecture.getIODeclarations()){
if (ioDeclaration.getArrayLength() == 1){
checkSingleIO(ioDeclaration);
}
else {
checkIOArray(ioDeclaration);
}
}
}
......
......@@ -20,26 +20,36 @@
*/
package de.monticore.lang.monticar.cnnarch._cocos;
import de.monticore.lang.monticar.cnnarch._ast.ASTIODeclaration;
import de.monticore.lang.monticar.cnnarch._ast.ASTIOLayer;
import de.monticore.lang.monticar.cnnarch._symboltable.IODeclarationSymbol;
import de.monticore.lang.monticar.cnnarch.helper.ErrorCodes;
import de.se_rwth.commons.logging.Log;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class CheckIOName implements CNNArchASTIODeclarationCoCo {
public class CheckIOName implements CNNArchASTIOLayerCoCo {
Set<String> ioNames = new HashSet<>();
private Set<IODeclarationSymbol> checkedIODeclarations = new HashSet<>();
@Override
public void check(ASTIODeclaration node) {
if (ioNames.contains(node.getName())){
Log.error("0" + ErrorCodes.DUPLICATED_NAME + " Duplicated IO name. " +
"The name '" + node.getName() + "' is already used."
public void check(ASTIOLayer node) {
Collection<IODeclarationSymbol> ioDeclarations = node.getEnclosingScope().get().<IODeclarationSymbol>resolveMany(node.getName(), IODeclarationSymbol.KIND);
if (ioDeclarations.isEmpty()){
Log.error("0" + ErrorCodes.UNKNOWN_IO + " Unknown input or output name. " +
"The input or output '" + node.getName() + "' does not exist"
, node.get_SourcePositionStart());
}
else {
ioNames.add(node.getName());
else if (ioDeclarations.size() > 1){
IODeclarationSymbol ioDeclaration = ioDeclarations.iterator().next();
if (!checkedIODeclarations.contains(ioDeclaration)){
Log.error("0" + ErrorCodes.DUPLICATED_NAME + " Duplicated IO name. " +
"The name '" + ioDeclaration.getName() + "' is already used."
, ioDeclaration.getSourcePosition());
checkedIODeclarations.addAll(ioDeclarations);
}
}
}
......
......@@ -20,59 +20,62 @@
*/
package de.monticore.lang.monticar.cnnarch._cocos;
import de.monticore.lang.monticar.cnnarch._ast.ASTArchType;
import de.monticore.lang.monticar.cnnarch._ast.ASTDimensionArgument;
import de.monticore.lang.monticar.cnnarch._ast.ASTArchitecture;
import de.monticore.lang.monticar.cnnarch._symboltable.ArchSimpleExpressionSymbol;
import de.monticore.lang.monticar.cnnarch._symboltable.ArchTypeSymbol;
import de.monticore.lang.monticar.cnnarch._symboltable.ArchitectureSymbol;
import de.monticore.lang.monticar.cnnarch._symboltable.IODeclarationSymbol;
import de.monticore.lang.monticar.cnnarch.helper.ErrorCodes;
import de.se_rwth.commons.logging.Log;
import java.util.Optional;
public class CheckIOShape implements CNNArchASTArchTypeCoCo {
public class CheckIOType implements CNNArchASTArchitectureCoCo {
@Override
public void check(ASTArchType node) {
boolean hasHeight = false;
boolean hasWidth = false;
boolean hasChannels = false;
for (ASTDimensionArgument dimensionArg : node.getShape().getDimensions()){
if (dimensionArg.getWidth().isPresent()){
if (hasWidth){
repetitionError(dimensionArg);
}
hasWidth = true;
}
else if (dimensionArg.getHeight().isPresent()){
if (hasHeight){
repetitionError(dimensionArg);
}
hasHeight = true;
}
else {
if (hasChannels){
repetitionError(dimensionArg);
}
hasChannels = true;
}
public void check(ASTArchitecture node) {
ArchitectureSymbol architecture = (ArchitectureSymbol) node.getSymbol().get();
for (IODeclarationSymbol ioDeclaration : architecture.getIODeclarations()){
check(ioDeclaration);
}
}
public void check(IODeclarationSymbol ioDeclaration) {
ArchTypeSymbol type = ioDeclaration.getType();
ArchTypeSymbol shape = (ArchTypeSymbol) node.getSymbol().get();
for (ArchSimpleExpressionSymbol dimension : shape.getDimensionSymbols()){
Optional<Integer> value = dimension.getIntValue();
if (!value.isPresent() || value.get() <= 0){
Log.error("0" + ErrorCodes.INVALID_IO_SHAPE + " Invalid shape. " +
"The dimensions can only be defined by a positive integer."
, dimension.getSourcePosition());
if (type.getElementType().isIsComplex() || type.getElementType().isIsBoolean()){
Log.error("0" + ErrorCodes.INVALID_IO_TYPE + " Invalid IO element type. " +
"Type has to be rational or whole number.");
}
if (type.getDimensionSymbols().size() == 2 || type.getDimensionSymbols().size() > 3){
Log.error("0" + ErrorCodes.INVALID_IO_TYPE + " Invalid dimension shape. Shape has to be either of size 1 or 3 (e.g. {number_of_channels, height, width})."
, ioDeclaration.getSourcePosition());
}
else {
for (ArchSimpleExpressionSymbol dimension : type.getDimensionSymbols()){
Optional<Integer> value = dimension.getIntValue();
if (!value.isPresent() || value.get() <= 0){
Log.error("0" + ErrorCodes.INVALID_IO_TYPE + " Invalid shape. " +
"The dimension sizes can only be positive integers."
, dimension.getSourcePosition());
}
}
}
}
private void repetitionError(ASTDimensionArgument node){
Log.error("0" + ErrorCodes.INVALID_IO_SHAPE + " Invalid shape. " +
"The dimension '" + node.getName().get() + "' was defined multiple times. "
, node.get_SourcePositionStart());
if (Log.getFindings().isEmpty()){
if (ioDeclaration.isInput() && type.getChannels() != 3 && type.getChannels() != 1){
if (type.getHeight() > 1 || type.getWidth() > 1){
Log.warn("The number of channels of input '" +
ioDeclaration.getName() + "' is: " + type.getChannels() +
". Heigth: " + type.getHeight() + ". Width: " + type.getWidth() + ". " +
"This is unusual and a sign of an error. " +
"The standard data format of this language is CHW and not HWC. "
, ioDeclaration.getSourcePosition());
}
}
}
}
}
......@@ -20,29 +20,19 @@
*/
package de.monticore.lang.monticar.cnnarch._cocos;
import de.monticore.lang.monticar.cnnarch._ast.ASTIOLayer;
import de.monticore.lang.monticar.cnnarch._symboltable.CompositeLayerSymbol;
import de.monticore.lang.monticar.cnnarch._ast.ASTIODeclaration;
import de.monticore.lang.monticar.cnnarch._symboltable.IODeclarationSymbol;
import de.monticore.lang.monticar.cnnarch._symboltable.IOLayerSymbol;
import de.monticore.lang.monticar.cnnarch._symboltable.LayerSymbol;
import de.monticore.lang.monticar.cnnarch.helper.ErrorCodes;
import de.monticore.symboltable.Symbol;
import de.se_rwth.commons.logging.Log;
import java.util.Collection;
import java.util.Collections;
public class CheckUnknownIO implements CNNArchASTIOLayerCoCo {
public class CheckUnusedASTIODeclaration implements CNNArchASTIODeclarationCoCo {
@Override
public void check(ASTIOLayer node) {
Symbol symbol = node.getSymbol().get();
Collection<IODeclarationSymbol> ioDeclarations = node.getEnclosingScope().get().<IODeclarationSymbol>resolveMany(node.getName(), IODeclarationSymbol.KIND);
if (ioDeclarations.isEmpty()){
Log.error("0" + ErrorCodes.UNKNOWN_IO + " Unknown input or output name. " +
"The input or output '" + node.getName() + "' does not exist"
, node.get_SourcePositionStart());
public void check(ASTIODeclaration node) {
IODeclarationSymbol ioDeclaration = (IODeclarationSymbol) node.getSymbol().get();
if (ioDeclaration.getConnectedLayers().isEmpty()){
Log.error("0" + ErrorCodes.MISSING_IO + " Input or output with name '" + ioDeclaration.getName() + "' was declared but not used. "
, ioDeclaration.getSourcePosition());
}
}
......
......@@ -60,7 +60,7 @@ public class ArchTypeSymbol extends CommonSymbol {
return heightIndex;
}
protected void setHeightIndex(int heightIndex) {
public void setHeightIndex(int heightIndex) {
this.heightIndex = heightIndex;
}
......@@ -68,7 +68,7 @@ public class ArchTypeSymbol extends CommonSymbol {
return widthIndex;
}
protected void setWidthIndex(int widthIndex) {
public void setWidthIndex(int widthIndex) {
this.widthIndex = widthIndex;
}
......@@ -76,7 +76,7 @@ public class ArchTypeSymbol extends CommonSymbol {
return channelIndex;
}
protected void setChannelIndex(int channelIndex) {
public void setChannelIndex(int channelIndex) {
this.channelIndex = channelIndex;
}
......
/**
*
* ******************************************************************************
* MontiCAR Modeling Family, www.se-rwth.de
* Copyright (c) 2017, Software Engineering Group at RWTH Aachen,
* All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* *******************************************************************************
*/
package de.monticore.lang.monticar.cnnarch._symboltable;
import de.monticore.symboltable.SymbolKind;
public class ArchitectureKind implements SymbolKind {
private static final String NAME = "de.monticore.lang.monticar.cnnarch._symboltable.ArchitectureKind";
@Override
public String getName() {
return NAME;
}
@Override
public boolean isKindOf(SymbolKind kind) {
return NAME.equals(kind.getName()) || SymbolKind.super.isKindOf(kind);
}
}
\ No newline at end of file
......@@ -24,28 +24,24 @@
package de.monticore.lang.monticar.cnnarch._symboltable;
import de.monticore.lang.monticar.cnnarch.helper.ErrorCodes;
import de.monticore.symboltable.CommonScopeSpanningSymbol;
import de.monticore.symboltable.Scope;
import de.se_rwth.commons.Joiners;
import de.se_rwth.commons.logging.Log;
import org.jscience.mathematics.number.Rational;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.*;
public class ArchitectureSymbol extends ArchitectureSymbolTOP {
public class ArchitectureSymbol extends CommonScopeSpanningSymbol {
private LayerSymbol body;
private List<IODeclarationSymbol> inputs;
private List<IODeclarationSymbol> outputs;
private List<VariableSymbol> parameters;
public static final ArchitectureKind KIND = new ArchitectureKind();
public ArchitectureSymbol(String name) {
super(name);
}
private LayerSymbol body;
private List<IOLayerSymbol> inputs = new ArrayList<>();
private List<IOLayerSymbol> outputs = new ArrayList<>();
@Override
protected ArchitectureScope createSpannedScope() {
return new ArchitectureScope();
public ArchitectureSymbol() {
super("", KIND);
}
public LayerSymbol getBody() {
......@@ -56,50 +52,35 @@ public class ArchitectureSymbol extends ArchitectureSymbolTOP {
this.body = body;
}
public List<IODeclarationSymbol> getInputs() {
public List<IOLayerSymbol> getInputs() {
return inputs;
}
protected void setInputs(List<IODeclarationSymbol> inputs) {
this.inputs = inputs;
}
public List<IODeclarationSymbol> getOutputs() {
public List<IOLayerSymbol> getOutputs() {
return outputs;
}
protected void setOutputs(List<IODeclarationSymbol> outputs) {
this.outputs = outputs;