Tensorflow + Go, the gopher way

tfgo: TensorFlow in Go

GoDoc Build Status

TensorFlow's Go bindings are hard to use: tfgo makes it easy!

No more problems like:

  • Scoping: each new node will have a new and unique name
  • Typing: attributes are automatically converted to a supported type instead of throwing errors at runtime

Also, it uses Method chaining making possible to write pleasant Go code.

Dependencies

  1. TensorFlow-2.4.0 lib. How to install tensorflow.
  2. TensorFlow bindings github.com/galeone/tensorflow. In order to correctly work with TensorFlow 2.4 in Go, we have to use a fork I created with some fix for the Go bindings. Bindings can be too large for go mod proxy, so you may want to switch off proxy usage by executing go env -w GONOSUMDB="github.com/galeone/tensorflow" to pull code directly using system installed git. It changes nothing in the user interface -- you can use go modules as usual.

Installation

go get github.com/galeone/tfgo

Getting started

The core data structure of the TensorFlow's Go bindings is the op.Scope struct. tfgo allows creating new *op.Scope that solves the scoping issue mentioned above.

Since we're defining a graph, let's start from its root (empty graph)

root := tg.NewRoot()

We can now place nodes into this graphs and connect them. Let's say we want to multiply a matrix for a column vector and then add another column vector to the result.

Here's the complete source code.

package main

import (
        "fmt"
        tg "github.com/galeone/tfgo"
        tf "github.com/galeone/tensorflow/tensorflow/go"
)

func main() {
        root := tg.NewRoot()
        A := tg.NewTensor(root, tg.Const(root, [2][2]int32{{1, 2}, {-1, -2}}))
        x := tg.NewTensor(root, tg.Const(root, [2][1]int64{{10}, {100}}))
        b := tg.NewTensor(root, tg.Const(root, [2][1]int32{{-10}, {10}}))
        Y := A.MatMul(x.Output).Add(b.Output)
        // Please note that Y is just a pointer to A!

        // If we want to create a different node in the graph, we have to clone Y
        // or equivalently A
        Z := A.Clone()
        results := tg.Exec(root, []tf.Output{Y.Output, Z.Output}, nil, &tf.SessionOptions{})
        fmt.Println("Y: ", results[0].Value(), "Z: ", results[1].Value())
        fmt.Println("Y == A", Y == A) // ==> true
        fmt.Println("Z == A", Z == A) // ==> false
}

that produces

Y:  [[200] [-200]] Z:  [[200] [-200]]
Y == A true
Z == A false

The list of the available methods is available on GoDoc: http://godoc.org/github.com/galeone/tfgo

Computer Vision using data flow graph

TensorFlow is rich of methods for performing operations on images. tfgo provides the image package that allows using the Go bindings to perform computer vision tasks in an elegant way.

For instance, it's possible to read an image, compute its directional derivative along the horizontal and vertical directions, compute the gradient and save it.

The code below does that, showing the different results achieved using correlation and convolution operations.

package main

import (
        tg "github.com/galeone/tfgo"
        "github.com/galeone/tfgo/image"
        "github.com/galeone/tfgo/image/filter"
        "github.com/galeone/tfgo/image/padding"
        tf "github.com/galeone/tensorflow/tensorflow/go"
        "os"
)

func main() {
        root := tg.NewRoot()
        grayImg := image.Read(root, "/home/pgaleone/airplane.png", 1)
        grayImg = grayImg.Scale(0, 255)

        // Edge detection using sobel filter: convolution
        Gx := grayImg.Clone().Convolve(filter.SobelX(root), image.Stride{X: 1, Y: 1}, padding.SAME)
        Gy := grayImg.Clone().Convolve(filter.SobelY(root), image.Stride{X: 1, Y: 1}, padding.SAME)
        convoluteEdges := image.NewImage(root.SubScope("edge"), Gx.Square().Add(Gy.Square().Value()).Sqrt().Value()).EncodeJPEG()

        Gx = grayImg.Clone().Correlate(filter.SobelX(root), image.Stride{X: 1, Y: 1}, padding.SAME)
        Gy = grayImg.Clone().Correlate(filter.SobelY(root), image.Stride{X: 1, Y: 1}, padding.SAME)
        correlateEdges := image.NewImage(root.SubScope("edge"), Gx.Square().Add(Gy.Square().Value()).Sqrt().Value()).EncodeJPEG()

        results := tg.Exec(root, []tf.Output{convoluteEdges, correlateEdges}, nil, &tf.SessionOptions{})

        file, _ := os.Create("convolved.png")
        file.WriteString(results[0].Value().(string))
        file.Close()

        file, _ = os.Create("correlated.png")
        file.WriteString(results[1].Value().(string))
        file.Close()
}

airplane.png

airplane

convolved.png

convolved

correlated.png

correlated

the list of the available methods is available on GoDoc: http://godoc.org/github.com/galeone/tfgo/image

Train in Python, Serve in Go

TensorFlow 2 comes with a lot of easy way to export a computational graph (e.g. Keras model, or a function decorated with @tf.function) to the SavedModel serialization format (that's the only one officially supported).

saved model

Using TensorFlow 2 (with Keras or tf.function) + tfgo, exporting a trained model (or a generic computational graph) and use it in Go is straightforward.

Just dig into the example to understand how to serve a trained model with tfgo.

Python code

import tensorflow as tf

model = tf.keras.Sequential(
    [
        tf.keras.layers.Conv2D(
            8,
            (3, 3),
            strides=(2, 2),
            padding="valid",
            input_shape=(28, 28, 1),
            activation=tf.nn.relu,
            name="inputs",
        ),  # 14x14x8
        tf.keras.layers.Conv2D(
            16, (3, 3), strides=(2, 2), padding="valid", activation=tf.nn.relu
        ),  # 7x716
        tf.keras.layers.Flatten(),
        tf.keras.layers.Dense(10, name="logits"),  # linear
    ]
)

tf.saved_model.save(model, "output/keras")

Go code

package main

import (
        "fmt"
        tg "github.com/galeone/tfgo"
        tf "github.com/galeone/tensorflow/tensorflow/go"
)

func main() {
        // A model exported with tf.saved_model.save()
        // automatically comes with the "serve" tag because the SavedModel
        // file format is designed for serving.
        // This tag contains the various functions exported. Among these, there is
        // always present the "serving_default" signature_def. This signature def
        // works exactly like the TF 1.x graph. Get the input tensor and the output tensor,
        // and use them as placeholder to feed and output to get, respectively.

        // To get info inside a SavedModel the best tool is saved_model_cli
        // that comes with the TensorFlow Python package.

        // e.g. saved_model_cli show --all --dir output/keras
        // gives, among the others, this info:

        // signature_def['serving_default']:
        // The given SavedModel SignatureDef contains the following input(s):
        //   inputs['inputs_input'] tensor_info:
        //       dtype: DT_FLOAT
        //       shape: (-1, 28, 28, 1)
        //       name: serving_default_inputs_input:0
        // The given SavedModel SignatureDef contains the following output(s):
        //   outputs['logits'] tensor_info:
        //       dtype: DT_FLOAT
        //       shape: (-1, 10)
        //       name: StatefulPartitionedCall:0
        // Method name is: tensorflow/serving/predict

        model := tg.LoadModel("test_models/output/keras", []string{"serve"}, nil)

        fakeInput, _ := tf.NewTensor([1][28][28][1]float32{})
        results := model.Exec([]tf.Output{
                model.Op("StatefulPartitionedCall", 0),
        }, map[tf.Output]*tf.Tensor{
                model.Op("serving_default_inputs_input", 0): fakeInput,
        })

        predictions := results[0]
        fmt.Println(predictions.Value())
}

Why?

Thinking about computation represented using graphs, describing computing in this way is, in one word, challenging.

Also, tfgo brings GPU computations to Go and allows writing parallel code without worrying about the device that executes it (just place the graph into the device you desire: that's it!)

Contribute

I love contributions. Seriously. Having people that share your same interests and want to face your same challenges it's something awesome.

If you'd like to contribute, just dig in the code and see what can be added or improved. Start a discussion opening an issue and let's talk about it.

Just follow the same design I use into the image package ("override" the same Tensor methods, document the methods, test your changes, ...)

There are a lot of packages that can be added, like the image package. Feel free to work on a brand new package: I'd love to see this kind of contributions!

TensorFlow installation

Manual

Download and install the C library from https://www.tensorflow.org/install/lang_c

curl -L "https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-cpu-linux-x86_64-2.4.0.tar.gz" | sudo tar -C /usr/local -xz
sudo ldconfig

Docker

docker pull tensorflow/tensorflow:2.4.0

Or you can use system package manager.

Owner
Paolo Galeone
Researcher in love with: Machine/Deep Learning, PostgreSQL, Tensorflow and Go. Owner of @nerdzeu
Paolo Galeone
Comments
  • Embedding a Python interpreter - Tensorflow Python API support

    Embedding a Python interpreter - Tensorflow Python API support

    I'm looking for collaborators for making tfgo a Tensorflow Go API 1:1 Python compatible.

    A brief recap:

    What is tfgo

    tfgo is a wrapper built around the Tensorflow Go bindings that allow:

    • defining graphs using method chaining in order to build graphs exactly as everyone is used to think about them (as a flow that goes from the input to the output)
    • importing a SavedModel and execute it
    • using some high-level operations built upon the low-level Go bindings (e.g. tfgo/image).

    It can successfully be used to run input pre-processing + inference or to define dataflow graph directly in Go. See the README.

    The problem:

    tfgo is not a 1:1 mapping with the Python API since the tensorflow/op package only exposes some of the C++ primitives and the Tensorflow Python API is way more complex and complete of the C++ API.

    People are familiar in using Tensorflow in Python and moreover, there are certain objects like the optimizers, the Variables, the Keras layers, the Keras losses, and so on that are defined in Python only.

    Therefore tfgo has a limited utility. There have been several people that asked me to add the tf.Variable support (the last one https://github.com/galeone/tfgo/issues/13 ) since they would be interested in training the models directly in Go and more in general, to have a 1:1 mapping between the Python functionalities and the tfgo functionalities.

    The idea

    tfgo can execute SavedModel objects and right now we can use Python to define a computational graph even to train a model, exporting a SavedModel that describes the whole training phase and execute it in tfgo (just define the model + the optimizer + the training operation and export this graph).

    Wouldn't be cool to make it possible to define the graph from a Go program, but with the complete support of the Python API?

    The idea is to embed a Python interpreter (ideally 3, 2 if there are technological constraints) inside tfgo, create a Go API that correctly communicates with the interpreter what the user wants and that builds a SavedModel. After that, tfgo then reads and executes it (exactly like the tf.Session in Python).

    e.g. if the user asks for something the Go package can do, just build the Graph using the Go bindings/tfgo - otherwise, use the Python interpreter.

    Tensorflow 2.0

    The Tensorflow Python API contains the decorator @tf.function Using this decorator it is possible to convert in its graph representation every correctly defined function (that uses the tf. primitives for everything).

    This could help a lot in implementing the idea since we can just:

    1. build the Python function body from Go
    2. decorating it using @tf.function
    3. export its graph representation in a SavedModel
    4. load it in tfgo for the execution

    Like we were used to working in Tensorflow < 2, with the graph-definition + session execution paradigm.

    Thoughts? Suggestions?

  • How to use the model trained by tf.estimator

    How to use the model trained by tf.estimator

    For example, when I used tf.estimator training model, input_fn was used in training and feature_column was specified, so there was no name of input node and output node, could you help me?

    est = tf.estimator.LinearClassifier(feature_columns=one_hot_feature_columns + crossed_columns,
                                                n_classes=2,
                                                model_dir=model_path,
                                                optimizer=tf.train.FtrlOptimizer(
                                                    learning_rate=0.01,
                                                    l2_regularization_strength=0.02)
                                                )
    
  • Support for Keras/TF_2.0?

    Support for Keras/TF_2.0?

    Hello,

    I am working in Python and using TensorFlow's Keras API ( e.g. tf.keras.models.Sequential). Would I be able to export it (export_savedmodel) and then load it into Go with TFGO? Or do I need to stick to "pure" TensorFlow expressions?

    Also, if I use TensorFlow 2.0, will it work correctly with TFGO?

    Thanks!

  • [Help needed]input must be 4-dimensional

    [Help needed]input must be 4-dimensional

    My code as below,

    		model := tg.LoadModel("/opt/models/mobilenet_v2_140_224", []string{"serve"}, nil)
    		scope := tg.NewRoot()
    		img := image.Read(scope, "/tmp/test.jpeg", 3)
    		// img = img.Clone().ResizeArea(image.Size{Height: 224, Width: 224}).Center()
    		input := tg.Exec(scope, []tf.Output{img.Value()}, nil, &tf.SessionOptions{})
    		tensor, err := tf.NewTensor(input[0].Value())
    		if err != nil {
    			log.Error().Err(err).Msg("")
    		}
    		results := model.Exec(
    			[]tf.Output{model.Op("StatefulPartitionedCall", 0)},
    			map[tf.Output]*tf.Tensor{model.Op("serving_default_input", 0): tensor},
    		)
    		log.Debug().Interface("results", results[0].Value()).Msg("")
    
    

    Result with:

    panic: input must be 4-dimensional[573,860,3]
    	 [[{{node predict/MobilenetV2/Conv/Relu6}}]]
    
    

    Could some one help me to fixed this issue?

    BTW: Same image file and model can be passed using tensorflow official go package.

  • Slow inference

    Slow inference

    First, thanks so much for working on this library! I'm using it for serving a couple of models with good success.

    I ran into something that surprised me today; it's not necessarily an issue with this library, but wanted to get your thoughts. I'm comparing execution of an SSD detector via a python script and a go executable. Just comparing the elapsed wall clock time with unix time command, the go executable is quite a bit faster -- around 4 seconds vs around 13 seconds for the python script. However, I wanted finer-grained timing data, so I measured just the model.Exec call in go vs the model.predict call in python, and I found that the go version is roughly twice as slow (3 sec vs 1.7 sec). My guess is that for a single run, parsing all the python code accounts for all of the extra time.

    Both of these are using the same model, although the python version is defining the model in code and loading weights from an hdf5 file, while the go version is loading model + weights from the SavedModel file (.pb format) -- not sure if that would make any difference.

    Do you have any ideas about why the graph execution would be slower under go, or how I could speed it up?

    Thanks!

  • installation issue: can't find for_core_protos_go_proto

    installation issue: can't find for_core_protos_go_proto

    go get github.com/galeone/tfgo go: finding module for package github.com/tensorflow/tensorflow/tensorflow/go/core/protobuf/for_core_protos_go_proto go: finding module for package github.com/tensorflow/tensorflow/tensorflow/go/core/protobuf/for_core_protos_go_proto ../../../../.gvm/pkgsets/go1.15/global/pkg/mod/github.com/tensorflow/[email protected]+incompatible/tensorflow/go/saved_model.go:25:2: module github.com/tensorflow/tensorflow@latest found (v2.4.1+incompatible), but does not contain package github.com/tensorflow/tensorflow/tensorflow/go/core/protobuf/for_core_protos_go_proto

  • How to use TensorFlow's Universal Sentence Encoder

    How to use TensorFlow's Universal Sentence Encoder

    How would I load in the universal-sentence-encoder-large embedding model?

    In Python

    embed = hub.Module("https://tfhub.dev/google/universal-sentence-encoder-large/3")
    embeddings = embed([
        "The quick brown fox jumps over the lazy dog.",
        "I am a sentence for which I would like to get its embedding"])
    
    print session.run(embeddings)
    

    In GO I've tried

    model, err := tf.LoadSavedModel("universal-sentence-encoder-large", []string{"serve"}, nil)
    
    if err != nil {
        fmt.Printf("Error loading saved model: %s\n", err.Error())
        return
    }
    

    but the program panics a when trying to load in the model 😕

    when I use the saved_model_cli I get empty results

    The given SavedModel contains the following tag-sets:
    
    

    How would I use the model? The directory looks like:

    ├── assets
    ├── saved_model.pb
    ├── tfhub_module.pb
    └── variables
    

    and the data was downloaded and unzipped from https://tfhub.dev/google/universal-sentence-encoder-large/3?tf-hub-format=compressed

  • Matching between output tensor name and prediction

    Matching between output tensor name and prediction

    I trained a multi-task model that has multiple outputs. running saved_model_cli gives the following output signatures The given SavedModel SignatureDef contains the following output(s):

        outputs['A'] tensor_info:
            dtype: DT_FLOAT
            shape: (-1, 1)
            name: StatefulPartitionedCall:0
        outputs['B'] tensor_info:
            dtype: DT_FLOAT
            shape: (-1, 1)
            name: StatefulPartitionedCall:1
        outputs['C'] tensor_info:
            dtype: DT_FLOAT
            shape: (-1, 1)
            name: StatefulPartitionedCall:2
        outputs['D'] tensor_info:
            dtype: DT_FLOAT
            shape: (-1, 1)
            name: StatefulPartitionedCall:3
      Method name is: tensorflow/serving/predict
    

    When using tfgo to do inference, I cannot find an easy way to match the prediction back to the output tensor name, i.e. ['A', 'B', 'C', 'D']

    res1 := p.Model.Exec([]tf.Output{
    		p.Model.Op("StatefulPartitionedCall", 1),
    	}, input)
    

    How can I map res1 to the prediction of B?

  • Invalid argument: shape must be a vector of {int32,int64}, got shape []

    Invalid argument: shape must be a vector of {int32,int64}, got shape []

    I try to use the interface of RandomUniform as follows:

    func TestRandomUniform(t *testing.T) {
    	root := op.NewScope()
    
    	T := op.Placeholder(root.SubScope("input"), tf.Int64)
    
    	seed := op.RandomUniformSeed(-1.0)
    	seed2 := op.RandomUniformSeed2(1.0)
    	product := op.RandomUniform(root,T,tf.Float,seed,seed2)
    
    
    	graph, err := root.Finalize()
    	if err != nil {
    		panic(err.Error())
    	}
    
    	var sess *tf.Session
    	sess, err = tf.NewSession(graph, &tf.SessionOptions{})
    	if err != nil {
    		panic(err.Error())
    	}
    
    	var A *tf.Tensor
    
    	if A, err = tf.NewTensor(int64(1)); err != nil {
    		panic(err.Error())
    	}
    
    	var results []*tf.Tensor
    	if results, err = sess.Run(
    		map[tf.Output]*tf.Tensor{
    			T: A,
    		},
    		[]tf.Output{product}, nil); err != nil {
    			panic(err.Error())
    	}
    	for _, result := range results {
    		fmt.Println(result.Value().([]int64))
    	}
    
    }
    
    Invalid argument: shape must be a vector of {int32,int64}, got shape []
    panic: shape must be a vector of {int32,int64}, got shape []
    	 [[Node: RandomUniform = RandomUniform[T=DT_INT64, _class=[], dtype=DT_FLOAT, seed=-1, seed2=1, _device="/job:localhost/replica:0/task:0/cpu:0"](_arg_input/Placeholder_0_0)]] [recovered]
    	panic: shape must be a vector of {int32,int64}, got shape []
    	 [[Node: RandomUniform = RandomUniform[T=DT_INT64, _class=[], dtype=DT_FLOAT, seed=-1, seed2=1, _device="/job:localhost/replica:0/task:0/cpu:0"](_arg_input/Placeholder_0_0)]]
    
    goroutine 5 [running]
    

    But it prints the error for me. What's it means?I am not good at c++. And i try to use your tfgo.I get the same result.

  • go get return error

    go get return error "cannot find package"

    anyone can help please. trying go get go get github.com/tensorflow/tensorflow/tensorflow/go

    getting an error "cannot find package..."

    I know that's not the appropriate place to ask but what i'm doing wrong ?

    I need to load a h5 model into a golang app and make prediction base on this model

    tfgo is built on tensorflow/go but we can't go get it thanks a lot for any help

  • Reading jpg image from disk and passing as input to a pretrained model loaded from disk

    Reading jpg image from disk and passing as input to a pretrained model loaded from disk

    Hi @galeone and friends,

    I have a pretrained model saved on disk, and a jpeg image saved on disk as well. I load the model and read the image and pass the image to the model. If I do this using python + tensorflow + Pillow I get a completely different result compared to when I use tfgo. Here is my go code:

            model := tg.LoadModel(modelDirectory), []string{"serve"}, nil)
    
            img := tfimg.Read(root, imagePathOnLocalDisk, 3)
    	batch := tg.Batchify(root, []tf.Output{img.Value()})
    	results := tg.Exec(root, []tf.Output{batch}, nil, &tf.SessionOptions{})
    	inputImage, _ := tf.NewTensor(results[0].Value())
    
    	output := model.Exec([]tf.Output{
    		model.Op("StatefulPartitionedCall", 0),
    	}, map[tf.Output]*tf.Tensor{
    		model.Op("serving_default_input_2", 0): inputImage,
    	})
    

    Any comments, feedback, recommendations would be very much appreciated.

    • How do you verify that the model behaves the same way when it is moved from training (using python on training env with GPU and specific arch etc) to inference (using go, using cpu etc)?
    • what does that "serve" SessionOption does? I copied it from a code sample in the docs or another issue here I think? Any other options I need to pass?
  • Panic vs Error

    Panic vs Error

    Hi @galeone :) And as always, thank you for the hard work and for keeping the lights on.

    I was wondering if you think it is possible to reduce the number of panic calls in this repository? Would it be okay if I send you PRs that would remove the calls to panic and instead return an error? If you think that is a reasonable thing to do, I assume we might need a separate develop branch for a while since these will be breaking changes (I mean backward incompatible) due to the changes in the signature of these functions?

    One example is https://github.com/galeone/tfgo/blob/d89a5c7e31e1d876a46136e30e7891dbc4272b69/image/image.go#L74 where image.Read can return an error if the file extension is not recognized, rather than calling panic

  • Thread safety

    Thread safety

    Hi @galeone
    Thanks for all the hard work. Was wondering if tfgo.Model is "thread safe". Specifically can I call tfgo.Exec from different goroutines ? are there any considerations ? I assume I at least need to keep the scopes separate? Thank you again for keeping this repo up to date.

  • Potential memory leak on reloading model

    Potential memory leak on reloading model

    Hey guys,

    We use tfgo and notice an increase of memory usage each time our model gets reloaded. We have a running service which periodically checks whether the model got updated and reloads it. Now I wouldn't expect the memory usage to increase, since the model in memory should be replaced by the updated one.

    The code to load the model is

    // load model into memory
    	model := tg.LoadModel(
    		"path/to/our/model",
    		[]string{
    			"serve",
    		},
    		&tf.SessionOptions{},
    	)
    

    But our monitoring shows that the usage goes up every time the model gets reloaded (once per hour). I profiled the service with pprof and could not see that any of the internal components in our code has a significantly growing memory usage.

    Furthermore I built tensorflow 2.9.1 with debug symbols and wrote a small go app just reloading the model. I did this to check for memory leaks with memleak-bpfcc from https://github.com/iovisor/bcc. This gave me the following stack trace, which, I believe, shows that there is memory leaked

    	1770048 bytes in 9219 allocations from stack
    		operator new(unsigned long)+0x19 [libstdc++.so.6.0.28]
    		google::protobuf::internal::GenericTypeHandler<tensorflow::NodeDef>::New(google::protobuf::Arena*)+0x1c [libtensorflow_framework.so.2]
    		google::protobuf::internal::GenericTypeHandler<tensorflow::NodeDef>::NewFromPrototype(tensorflow::NodeDef const*, google::protobuf::Arena*)+0x20 [libtensorflow_framework.so.2]
    		google::protobuf::RepeatedPtrField<tensorflow::NodeDef>::TypeHandler::Type* google::protobuf::internal::RepeatedPtrFieldBase::Add<google::protobuf::RepeatedPtrField<tensorflow::NodeDef>::TypeHandler>(google::protobuf::RepeatedPtrField<tensorflow::NodeDef>::TypeHandler::Type*)+0xc2 [libtensorflow_framework.so.2]
    		google::protobuf::RepeatedPtrField<tensorflow::NodeDef>::Add()+0x21 [libtensorflow_framework.so.2]
    		tensorflow::FunctionDef::add_node_def()+0x20 [libtensorflow_framework.so.2]
    		tensorflow::FunctionDef::MergePartialFromCodedStream(google::protobuf::io::CodedInputStream*)+0x334 [libtensorflow_framework.so.2]
    		bool google::protobuf::internal::WireFormatLite::ReadMessage<tensorflow::FunctionDef>(google::protobuf::io::CodedInputStream*, tensorflow::FunctionDef*)+0x64 [libtensorflow_framework.so.2]
    		tensorflow::FunctionDefLibrary::MergePartialFromCodedStream(google::protobuf::io::CodedInputStream*)+0x240 [libtensorflow_framework.so.2]
    		bool google::protobuf::internal::WireFormatLite::ReadMessage<tensorflow::FunctionDefLibrary>(google::protobuf::io::CodedInputStream*, tensorflow::FunctionDefLibrary*)+0x64 [libtensorflow_framework.so.2]
    		tensorflow::GraphDef::MergePartialFromCodedStream(google::protobuf::io::CodedInputStream*)+0x291 [libtensorflow_framework.so.2]
    		bool google::protobuf::internal::WireFormatLite::ReadMessage<tensorflow::GraphDef>(google::protobuf::io::CodedInputStream*, tensorflow::GraphDef*)+0x64 [libtensorflow_framework.so.2]
    		tensorflow::MetaGraphDef::MergePartialFromCodedStream(google::protobuf::io::CodedInputStream*)+0x325 [libtensorflow_framework.so.2]
    		bool google::protobuf::internal::WireFormatLite::ReadMessage<tensorflow::MetaGraphDef>(google::protobuf::io::CodedInputStream*, tensorflow::MetaGraphDef*)+0x64 [libtensorflow_framework.so.2]
    		tensorflow::SavedModel::MergePartialFromCodedStream(google::protobuf::io::CodedInputStream*)+0x25b [libtensorflow_framework.so.2]
    		google::protobuf::MessageLite::MergeFromCodedStream(google::protobuf::io::CodedInputStream*)+0x32 [libtensorflow_framework.so.2]
    		google::protobuf::MessageLite::ParseFromCodedStream(google::protobuf::io::CodedInputStream*)+0x3e [libtensorflow_framework.so.2]
    		tensorflow::ReadBinaryProto(tensorflow::Env*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, google::protobuf::MessageLite*)+0x141 [libtensorflow_framework.so.2]
    		tensorflow::(anonymous namespace)::ReadSavedModel(absl::lts_20211102::string_view, tensorflow::SavedModel*)+0x136 [libtensorflow_framework.so.2]
    		tensorflow::ReadMetaGraphDefFromSavedModel(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::unordered_set<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, tensorflow::MetaGraphDef*)+0x5d [libtensorflow_framework.so.2]
    		tensorflow::LoadSavedModelInternal(tensorflow::SessionOptions const&, tensorflow::RunOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::unordered_set<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, tensorflow::SavedModelBundle*)+0x41 [libtensorflow_framework.so.2]
    		tensorflow::LoadSavedModel(tensorflow::SessionOptions const&, tensorflow::RunOptions const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::unordered_set<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::hash<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&, tensorflow::SavedModelBundle*)+0xc0 [libtensorflow_framework.so.2]
    		TF_LoadSessionFromSavedModel+0x2a8 [libtensorflow.so]
    		_cgo_6ae2e7a71f9a_Cfunc_TF_LoadSessionFromSavedModel+0x6e [testapp]
    		runtime.asmcgocall.abi0+0x64 [testapp]
    		github.com/galeone/tensorflow/tensorflow/go._Cfunc_TF_LoadSessionFromSavedModel.abi0+0x4d [testapp]
    		github.com/galeone/tensorflow/tensorflow/go.LoadSavedModel.func2+0x14f [testapp]
    		github.com/galeone/tensorflow/tensorflow/go.LoadSavedModel+0x2b6 [testapp]
    		github.com/galeone/tfgo.LoadModel+0x6d [testapp]
    		main.reloadModel+0x276 [testapp]
    		main.main+0x72 [testapp]
    		runtime.main+0x212 [testapp]
    		runtime.goexit.abi0+0x1 [testapp]
    
    
    

    As you can see this stacktrace shows calls to tfgo and to the underlying tensorflow library. I am not sure if I read it right, but it seems like there is a leak in tfgo or tensorflow itself.

    Is there a way to explicitly release the memory of a loaded model when we reload? Could it be a problem in tfgo? If you need more information on this, please tell me.

    Thanks in advance :)

  • tensor example support

    tensor example support

    It seems that it is not possible to use "github.com/galeone/tfgo/proto/example" from tfgo v2.9. Are there any available ways to use example.Features and example.Example in tfgo v2.9? Or could you let me know another way to feed saved models without them?

TFKG - A Tensorflow and Keras Golang port

TFKG - A Tensorflow and Keras Golang port This is experimental and quite nasty under the hood* Support macOS: running docker container, no GPU acceler

Oct 18, 2022
face detction/recognization golang lib using tensorflow facenet
face detction/recognization golang lib using tensorflow facenet

Golang lib for detect/recognize by tensorflow facenet Prerequest libtensorfow 1.x Follow the instruction Install TensorFlow for C facenet tenorflow sa

Sep 23, 2022
Gopher-translator - A HTTP API that accepts english word or sentences and translates them to Gopher language

Gopher Translator Service An interview assignment project. To see the full assig

Jan 25, 2022
Personal Photo Management powered by Go and Google TensorFlow
Personal Photo Management powered by Go and Google TensorFlow

PhotoPrism® is a privately hosted app for browsing, organizing, and sharing your photo collection. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.

Dec 29, 2022
Go binding for TensorFlow Lite
Go binding for TensorFlow Lite

go-tflite Go binding for TensorFlow Lite Usage model := tflite.NewModelFromFile("sin_model.tflite") if model == nil { log.Fatal("cannot load model")

Jan 1, 2023
TFKG - A Tensorflow and Keras Golang port

TFKG - A Tensorflow and Keras Golang port This is experimental and quite nasty under the hood* Support macOS: running docker container, no GPU acceler

Oct 18, 2022
face detction/recognization golang lib using tensorflow facenet
face detction/recognization golang lib using tensorflow facenet

Golang lib for detect/recognize by tensorflow facenet Prerequest libtensorfow 1.x Follow the instruction Install TensorFlow for C facenet tenorflow sa

Sep 23, 2022
A golang framework helps gopher to build a data visualization and admin panel in ten minutes
A golang framework helps gopher to build a data visualization and admin panel in ten minutes

the missing golang data admin panel builder tool. Documentation | 中文介绍 | DEMO | 中文DEMO | Twitter | Forum Inspired by laravel-admin Preface GoAdmin is

Dec 30, 2022
Gopher Artwork by Ashley McNamara
Gopher Artwork by Ashley McNamara

Readme - Gopher Artwork Based on original artwork from the amazing Renee French. You can also find us on Twitter: @ashleymcnamara @reneefrench How can

Dec 28, 2022
GopherSay allow you to display a message said by a cute random Gopher.

GopherSay About Welcome in GopherSay! GopherSay is inspired by Cowsay program. GopherSay allow you to display a message said by a cute random Gopher.

Nov 23, 2022
Gopher Care - Microapps with Go and gRPC-Web
 Gopher Care - Microapps with Go and gRPC-Web

Gopher Care - Microapps with Go and gRPC-Web Work in progress Deployed, Check it out! For more context, please read the <-(Ö_ö)-> before checking the

Oct 29, 2021
Gopher protocol library for Golang

Gopher protocol library for Golang This is a standards compliant Gopher library for the Go programming language implementing the RFC 1436 specificatio

Nov 13, 2021
Graphical small-internet client for windows, linux, MacOS X and BSDs. Supports gemini, http, https, gopher, finger.
Graphical small-internet client for windows, linux, MacOS X and BSDs. Supports gemini, http, https, gopher, finger.

Graphical small-internet client for windows, linux, MacOS X and BSDs. Supports gemini, http, https, gopher, finger.

Jan 1, 2023
A dataviz framework help gopher to build a admin panel in ten minutes
A dataviz framework help gopher to build a admin panel in ten minutes

the missing golang data admin panel builder tool. Documentation | 中文文档 | DEMO Inspired by laravel-admin Preface GoAdmin is a toolkit to help you build

Oct 17, 2022
Binarian - BinaryHack library for Gopher

Binarian BinaryHack library for Gopher Status WIP License MIT

Jan 12, 2022
Gopher-ml - Simple neural network built in go

Building a neural network from scratch with Golang Simply neural network built i

Jan 10, 2022
GoAdmin Instruction - A golang framework help gopher quickly build a data visualization platform

GoAdmin Instruction - A golang framework help gopher quickly build a data visualization platform

Jan 21, 2022
A 3D model of the Go Gopher mascot. We ❤️ Go.
A 3D model of the Go Gopher mascot. We  ❤️  Go.

Go Gopher model A 3D model of the Go Gopher mascot. We ❤ Go. Why? The conversation went something like this: Walter (Developer): Go uses a Gopher as a

Dec 22, 2022
skr: The lightweight and powerful web framework using the new way for Go.Another go the way.
skr: The lightweight and powerful web framework using the new way for Go.Another go the way.

skr Overview Introduction Documents Features Install Quickstart Releases Todo Pull Request Issues Thanks Introduction The lightweight and powerful web

Jan 11, 2022