소스 검색

Implement basic neural network visualization

Alexey Edelev 5 년 전
부모
커밋
25bb796f4a
12개의 변경된 파일467개의 추가작업 그리고 21개의 파일을 삭제
  1. 6 3
      gui/CMakeLists.txt
  2. 58 0
      gui/dense.cpp
  3. 47 0
      gui/dense.h
  4. 39 16
      gui/main.cpp
  5. 112 0
      gui/main.qml
  6. 5 0
      gui/qml.qrc
  7. 31 0
      gui/valueindicator.cpp
  8. 53 0
      gui/valueindicator.h
  9. 60 0
      gui/visualizermodel.cpp
  10. 49 0
      gui/visualizermodel.h
  11. 1 1
      neuralnetwork/main.go
  12. 6 1
      neuralnetwork/remotecontrol/remotecontrol.go

+ 6 - 3
gui/CMakeLists.txt

@@ -1,7 +1,7 @@
-project(NeuralNetworkUi)
-
 cmake_minimum_required(VERSION 2.8)
 
+project(NeuralNetworkUi LANGUAGES CXX)
+
 find_package(Qt5 COMPONENTS Quick Gui Core Qml REQUIRED)
 
 set(QTPROTOBUF_MAKE_TESTS false)
@@ -16,5 +16,8 @@ file(GLOB PROTO_FILES ABSOLUTE "${CMAKE_CURRENT_SOURCE_DIR}/../neuralnetwork/rem
 
 generate_qtprotobuf(TARGET NeuralNetworkUi PROTO_FILES ${PROTO_FILES})
 
-add_executable(NeuralNetworkUi main.cpp)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_AUTORCC ON)
+
+add_executable(NeuralNetworkUi main.cpp qml.qrc valueindicator.cpp visualizermodel.cpp dense.cpp)
 target_link_libraries(NeuralNetworkUi Qt5::Core Qt5::Gui Qt5::Qml Qt5::Quick QtProtobufProject::QtProtobuf QtProtobufProject::QtGrpc ${QtProtobuf_GENERATED})

+ 58 - 0
gui/dense.cpp

@@ -0,0 +1,58 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "dense.h"
+#include <QtGlobal>
+
+//0 -  3  Version = 1          (uint32)
+//4       'G'                  (byte)
+//5       'F'                  (byte)
+//6       'A'                  (byte)
+//7       0                    (byte)
+//8 - 15  number of rows       (int64)
+//16 - 23  number of columns    (int64)
+//24 - 31  0                    (int64)
+//32 - 39  0                    (int64)
+//40 - ..  matrix data elements (float64)
+//        [0,0] [0,1] ... [0,ncols-1]
+//        [1,0] [1,1] ... [1,ncols-1]
+//        ...
+//        [nrows-1,0] ... [nrows-1,ncols-1]
+
+Dense::Dense(const QByteArray &data)
+{
+    m_data = data;
+    m_rows = *(int64_t*)(data.data() + 8);
+    m_columns = *(int64_t*)(data.data() + 16);
+
+    Q_ASSERT(m_rows*m_columns + 40 == data.size());
+}
+
+double Dense::value(int row, int column)
+{
+    char* dataPtr = m_data.data() + 40 + m_rows * row + m_columns * column;
+    return *(double*)dataPtr;
+}
+

+ 47 - 0
gui/dense.h

@@ -0,0 +1,47 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <QByteArray>
+
+class Dense
+{
+public:
+    Dense(const QByteArray &data);
+    double value(int row, int column);
+
+    int rows() {
+        return m_rows;
+    }
+
+    int columns() {
+        return m_columns;
+    }
+private:
+    int m_rows;
+    int m_columns;
+    QByteArray m_data;
+};

+ 39 - 16
gui/main.cpp

@@ -1,11 +1,37 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
 #include <QGuiApplication>
-#include <QQmlEngine>
-#include <QQuickView>
+#include <QQmlApplicationEngine>
+#include <QQmlContext>
 #include <QDebug>
 
 #include "remotecontrolclient.h"
 #include "qgrpchttp2channel.h"
 #include "insecurecredentials.h"
+#include "visualizermodel.h"
 
 class NoneCredencials : public QtProtobuf::CallCredentials
 {
@@ -18,20 +44,17 @@ int main(int argc, char *argv[])
 {
     QGuiApplication app(argc, argv);
 
-    remotecontrol::RemoteControlClient client;
+    std::shared_ptr<remotecontrol::RemoteControlClient> client(new remotecontrol::RemoteControlClient);
     auto chan = std::shared_ptr<QtProtobuf::QGrpcHttp2Channel>(new QtProtobuf::QGrpcHttp2Channel(QUrl("http://localhost:65001"), QtProtobuf::InsecureCredentials()|NoneCredencials()));
-    client.attachChannel(chan);
-    QObject::connect(&client, &remotecontrol::RemoteControlClient::ActivationsUpdated, [](const remotecontrol::LayerMatrix &activations) {
-        qDebug() << "ActivationsUpdated:" << activations.layer();
-    });
-    QObject::connect(&client, &remotecontrol::RemoteControlClient::BiasesUpdated, [](const remotecontrol::LayerMatrix &biases) {
-        qDebug() << "BiasesUpdated:" << biases.layer();
-    });
-    QObject::connect(&client, &remotecontrol::RemoteControlClient::WeightsUpdated, [](const remotecontrol::LayerMatrix &weights) {
-        qDebug() << "WeightsUpdated:" << weights.layer();
-    });
-    client.subscribeActivationsUpdates({});
-    client.subscribeBiasesUpdates({});
-    client.subscribeWeightsUpdates({});
+    client->attachChannel(chan);
+
+    std::unique_ptr<VisualizerModel> visualizerModel(new VisualizerModel(client));
+
+    QQmlApplicationEngine engine;
+    engine.rootContext()->setContextProperty("visualizerModel", visualizerModel.get());
+    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+    if (engine.rootObjects().isEmpty())
+        return -1;
+
     return app.exec();
 }

+ 112 - 0
gui/main.qml

@@ -0,0 +1,112 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+import QtQuick 2.11
+import QtQuick.Window 2.11
+import QtQuick.Controls 1.4
+
+ApplicationWindow {
+    id: root
+    visible: true
+    width: 1024
+    height: 768
+
+    Rectangle {
+        id: bottomLayer
+        anchors.fill: parent
+        color: "#000000"
+    }
+
+    Row {
+        id: rowww
+        anchors.left: parent.left
+        anchors.top: parent.top
+        anchors.margins: 10
+        Repeater {
+            id: layerRepeater
+            model: visualizerModel.sizes.length
+            delegate: Item {
+                id: layerDelegate
+                property alias neurons: neuronRepeater
+                anchors.verticalCenter: parent.verticalCenter
+                property int layerIndex: model.index
+                property int layerSize: visualizerModel.sizes[layerIndex]
+                width: 120
+                height: activationsColumn.height
+                Column {
+                    id: activationsColumn
+                    width: 50
+                    spacing: 20
+                    Repeater {
+                        id: neuronRepeater
+                        model: layerSize
+                        delegate: Rectangle {
+                            id: neuron
+                            width: 30
+                            height: 30
+                            radius: 15
+                        }
+                    }
+                }
+            }
+        }
+        onWidthChanged: {
+            console.log("width: " + width)
+            if (width !== visualizerModel.sizes.length*120) {
+                return
+            }
+
+            for (var i = 1; i < layerRepeater.count; i++) {
+                var neurons = layerRepeater.itemAt(i).neurons
+                var neuronsPrev = layerRepeater.itemAt(i - 1).neurons
+                for (var j = 0; j < neurons.count; j++) {
+                    var neuron = neurons.itemAt(j)
+                    var coord = layerRepeater.itemAt(i).mapToItem(root.contentItem, neuron.x + neuron.width/2, neuron.y + neuron.height/2)
+                    for(var k = 0; k < neuronsPrev.count; k++) {
+                        var neuronPrev = neuronsPrev.itemAt(k)
+                        var coordPrev = layerRepeater.itemAt(i - 1).mapToItem(root.contentItem, neuronPrev.x + neuronPrev.width/2, neuronPrev.y + neuronPrev.height/2)
+                        var angle =  Math.atan2(coordPrev.y - coord.y, coordPrev.x - coord.x) * 180 / Math.PI
+                        var length = Math.sqrt(Math.pow(coordPrev.x - coord.x, 2) + Math.pow(coordPrev.y - coord.y, 2))
+                        connection.createObject(bottomLayer, {x: coord.x, y: coord.y, width: length, angle: angle})
+                    }
+                }
+            }
+        }
+    }
+
+    Component {
+        id: connection
+        Rectangle {
+            property alias angle: trans.angle
+            transformOrigin: Item.Left
+            transform: Rotation {
+                id: trans
+            }
+
+            height: 1
+            color: "#5500ff00"
+        }
+    }
+}

+ 5 - 0
gui/qml.qrc

@@ -0,0 +1,5 @@
+<RCC>
+    <qresource prefix="/">
+        <file>main.qml</file>
+    </qresource>
+</RCC>

+ 31 - 0
gui/valueindicator.cpp

@@ -0,0 +1,31 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "valueindicator.h"
+
+ValueIndicator::ValueIndicator()
+{
+
+}

+ 53 - 0
gui/valueindicator.h

@@ -0,0 +1,53 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <QObject>
+
+class ValueIndicator
+{
+    Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged)
+public:
+    ValueIndicator();
+    qreal value() const
+    {
+        return m_value;
+    }
+public slots:
+    void setValue(qreal value)
+    {
+        qWarning("Floating point comparison needs context sanity check");
+        if (qFuzzyCompare(m_value, value))
+            return;
+
+        m_value = value;
+        emit valueChanged(m_value);
+    }
+signals:
+    void valueChanged(qreal value);
+private:
+    qreal m_value;
+};

+ 60 - 0
gui/visualizermodel.cpp

@@ -0,0 +1,60 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "visualizermodel.h"
+
+#include <QDebug>
+
+#include <qgrpcasyncreply.h>
+
+#include "dense.h"
+
+using namespace remotecontrol;
+using namespace QtProtobuf;
+
+VisualizerModel::VisualizerModel(std::shared_ptr<RemoteControlClient> &client, QObject *parent) : QObject(parent)
+  , m_client(client)
+{
+    m_client->getConfiguration({}, this, [this](QGrpcAsyncReply *reply) {
+        m_networkConfig = reply->read<Configuration>();
+        sizesChanged();
+    });
+
+    QObject::connect(client.get(), &remotecontrol::RemoteControlClient::ActivationsUpdated, [](const remotecontrol::LayerMatrix &activations) {
+        Dense dense(activations.matrix().matrix());
+        qDebug() << "ActivationsUpdated:" << dense.rows() << dense.columns();
+    });
+    QObject::connect(client.get(), &remotecontrol::RemoteControlClient::BiasesUpdated, [](const remotecontrol::LayerMatrix &biases) {
+        Dense dense(biases.matrix().matrix());
+        qDebug() << "BiasesUpdated:" << dense.rows() << dense.columns();
+    });
+    QObject::connect(client.get(), &remotecontrol::RemoteControlClient::WeightsUpdated, [](const remotecontrol::LayerMatrix &weights) {
+        Dense dense(weights.matrix().matrix());
+        qDebug() << "WeightsUpdated:" << dense.rows() << dense.columns();
+    });
+    client->subscribeActivationsUpdates({});
+    client->subscribeBiasesUpdates({});
+    client->subscribeWeightsUpdates({});
+}

+ 49 - 0
gui/visualizermodel.h

@@ -0,0 +1,49 @@
+/*
+ * MIT License
+ *
+ * Copyright (c) 2019 Alexey Edelev <semlanik@gmail.com>
+ *
+ * This file is part of NeuralNetwork project https://git.semlanik.org/semlanik/NeuralNetwork
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this
+ * software and associated documentation files (the "Software"), to deal in the Software
+ * without restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software, and
+ * to permit persons to whom the Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies
+ * or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+ * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#pragma once
+
+#include <QObject>
+#include <QGenericMatrix>
+#include "remotecontrolclient.h"
+
+class VisualizerModel : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QList<int> sizes READ sizes NOTIFY sizesChanged)
+public:
+    explicit VisualizerModel(std::shared_ptr<remotecontrol::RemoteControlClient> &client, QObject *parent = nullptr);
+
+    QList<int> sizes() {
+        return m_networkConfig.sizes();
+    }
+
+signals:
+    void sizesChanged();
+
+private:
+    std::shared_ptr<remotecontrol::RemoteControlClient> &m_client;
+    remotecontrol::Configuration m_networkConfig;
+};

+ 1 - 1
neuralnetwork/main.go

@@ -11,7 +11,7 @@ import (
 )
 
 func main() {
-	sizes := []int{13, 8, 8, 8, 8, 3}
+	sizes := []int{13, 14, 14, 3}
 	nn, _ := neuralnetwork.NewNeuralNetwork(sizes, neuralnetwork.NewRPropInitializer(neuralnetwork.RPropConfig{
 		NuPlus:   1.2,
 		NuMinus:  0.5,

+ 6 - 1
neuralnetwork/remotecontrol/remotecontrol.go

@@ -95,7 +95,12 @@ func NewLayerMatrix(l int, dense *mat.Dense, contentType LayerMatrix_ContentType
 }
 
 func (rw *RemoteControl) GetConfiguration(context.Context, *None) (*Configuration, error) {
-	return nil, status.Error(codes.Unimplemented, "Not implemented")
+	config := &Configuration{}
+
+	for _, size := range rw.nn.Sizes {
+		config.Sizes = append(config.Sizes, int32(size))
+	}
+	return config, nil
 }
 
 func (rw *RemoteControl) Activations(_ *None, srv RemoteControl_ActivationsServer) error {