Skip to content

Commit

Permalink
gzipped protobuf compressed neural networks (#166)
Browse files Browse the repository at this point in the history
* gzipped protobuf compressed neural networks

* remove debug info and bump version (corresponds with tf)

* clang-format

* clang-format style=Google

* make protobuf a submodule

* add protobuf.wrap

* update submodule for appveyor

* update submodule for appveyor try 2

* update submodule using https

* fix pr comments

* add clang-format and fix format in loader

* store clangformat delta only

* update submodule

* various fixes

* check for v1 weights and conform google style

* temporary protobuf wrap until windows fixes are upstreamed

* add compiler to docker for circleci

* protobuf compiler for circleci

* protobuf may be installed without protoc

* [CircleCI] Install protobuf dependency on the docker image (#172)

* [CircleCI] Install protobuf dependency on the docker image

* [CircleCI] Add a step to pull git submodules

* [CircleCI] Use protoc version 3.5.1

* [CircleCI] Don't install protobuf deps from Ubuntu repos

* [CircleCI] Use the docker image with the latest changes

* pr review fixes

* update lczero-common

* check buffer size < 2

* moar pr comments
  • Loading branch information
Error323 committed Jul 15, 2018
1 parent d063d38 commit a530848
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 37 deletions.
9 changes: 7 additions & 2 deletions .circleci/Dockerfile
@@ -1,6 +1,11 @@
FROM floopcz/tensorflow_cc:ubuntu-shared-cuda:0.0.1
FROM floopcz/tensorflow_cc:ubuntu-shared-cuda

RUN wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB && apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS-2019.PUB && sh -c 'echo deb https://apt.repos.intel.com/mkl all main > /etc/apt/sources.list.d/intel-mkl.list' && apt-get update && apt-get install -y intel-mkl-64bit-2018.2-046
RUN apt-get install -y clang-6.0 ninja-build python3-pip nvidia-opencl-dev libopenblas-dev libboost-dev nvidia-cuda-dev nvidia-cuda-toolkit libgtest-dev libprotobuf-dev git ssh tar gzip ca-certificates sudo
RUN apt-get install -y clang-6.0 ninja-build python3-pip nvidia-opencl-dev libopenblas-dev libboost-dev nvidia-cuda-dev nvidia-cuda-toolkit libgtest-dev git ssh tar gzip ca-certificates sudo
RUN pip3 install meson
RUN ln -s /usr/include/ /usr/include/openblas

RUN curl -OL https://github.com/google/protobuf/releases/download/v3.5.1/protoc-3.5.1-linux-x86_64.zip
RUN unzip protoc-3.5.1-linux-x86_64.zip -d protoc3
RUN sudo mv protoc3/bin/* /usr/local/bin/
RUN sudo mv protoc3/include/* /usr/local/include/
7 changes: 6 additions & 1 deletion .circleci/config.yml
Expand Up @@ -2,9 +2,14 @@ version: 2
jobs:
build:
docker:
- image: danieluranga/leela_chess_zero-lc0_ubuntu_builder:0.0.1
- image: danieluranga/leela_chess_zero-lc0_ubuntu_builder:0.0.4
steps:
- checkout
- run:
name: "Pull Submodules"
command: |
git submodule init
git submodule update --remote
- run:
name: Build clang version
command: CC=clang-6.0 CXX=clang++-6.0 ./build.sh
Expand Down
6 changes: 6 additions & 0 deletions .clang-format
@@ -0,0 +1,6 @@
---
Language: Cpp
BasedOnStyle: Google
DerivePointerAlignment: false
...

3 changes: 3 additions & 0 deletions .gitmodules
@@ -0,0 +1,3 @@
[submodule "libs/lczero-common"]
path = libs/lczero-common
url = https://github.com/LeelaChessZero/lczero-common.git
1 change: 1 addition & 0 deletions appveyor.yml
Expand Up @@ -46,6 +46,7 @@ cache:
- C:\Python36\Lib\site-packages
- 'C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.2'
before_build:
- cmd: git submodule update --init --recursive
- cmd: meson.py build --backend vs2015 --buildtype release -Dgtest=%GTEST% -Dopencl=%OPENCL% -Dblas=%BLAS% -Dcudnn=%CUDA% -Dcudnn_include="%PKG_FOLDER%\cuda\include" -Dcudnn_libdirs="C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.2\lib\x64","%PKG_FOLDER%\cuda\lib\x64" -Dopenblas_include="%PKG_FOLDER%\OpenBLAS.0.2.14.1\lib\native\include" -Dopenblas_libdirs="%PKG_FOLDER%\OpenBLAS.0.2.14.1\lib\native\lib\x64" -Dopencl_include="%PKG_FOLDER%\opencl-nug.0.777.12\build\native\include" -Dopencl_libdirs="%PKG_FOLDER%\opencl-nug.0.777.12\build\native\lib\x64" -Ddefault_library=static
build:
project: build/lc0.sln
Expand Down
1 change: 1 addition & 0 deletions libs/lczero-common
Submodule lczero-common added at ab563d
12 changes: 12 additions & 0 deletions meson.build
Expand Up @@ -39,6 +39,18 @@ files = []
includes = []
has_backends = false

deps += dependency('protobuf', fallback : ['protobuf', 'protobuf_dep'], required: true)
protoc = find_program('protoc', required : false)
if not protoc.found()
message('protoc will be built from the subproject')
protoc = subproject('protobuf').get_variable('protoc')
endif

gen = generator(protoc, output: ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'],
arguments : ['--proto_path=@CURRENT_SOURCE_DIR@/libs/lczero-common', '--cpp_out=@BUILD_DIR@', '@INPUT@'])

files += gen.process('libs/lczero-common/proto/net.proto',
preserve_path_from : meson.current_source_dir() + '/libs/lczero-common/')

#############################################################################
## Main files
Expand Down
142 changes: 110 additions & 32 deletions src/neural/loader.cc
Expand Up @@ -24,20 +24,39 @@
#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include "utils/commandline.h"
#include "utils/exception.h"
#include "utils/filesystem.h"
#include "proto/net.pb.h"
#include "version.inc"



namespace lczero {

FloatVectors LoadFloatsFromFile(const std::string& filename) {
namespace {
void PopulateLastIntoVector(FloatVectors* vecs, Weights::Vec* out) {
*out = std::move(vecs->back());
vecs->pop_back();
}

void PopulateConvBlockWeights(FloatVectors* vecs, Weights::ConvBlock* block) {
PopulateLastIntoVector(vecs, &block->bn_stddivs);
PopulateLastIntoVector(vecs, &block->bn_means);
PopulateLastIntoVector(vecs, &block->biases);
PopulateLastIntoVector(vecs, &block->weights);
}

std::string DecompressGzip(const std::string& filename) {
const int kStartingSize = 8 * 1024 * 1024; // 8M
std::vector<char> buffer(kStartingSize);
std::string buffer;
buffer.resize(kStartingSize);
int bytes_read = 0;

// Read whole file into a buffer.
gzFile file = gzopen(filename.c_str(), "rb");
if (!file) throw Exception("Cannot read weights from " + filename);
if (!file) throw lczero::Exception("Cannot read weights from " + filename);
while (true) {
int sz = gzread(file, &buffer[bytes_read], buffer.size() - bytes_read);
if (sz == static_cast<int>(buffer.size()) - bytes_read) {
Expand All @@ -46,25 +65,92 @@ FloatVectors LoadFloatsFromFile(const std::string& filename) {
} else {
bytes_read += sz;
buffer.resize(bytes_read);
// Add newline in the end for the case it was not there.
buffer.push_back('\n');
break;
}
}
gzclose(file);

return buffer;
}

FloatVector DenormLayer(const pblczero::Weights_Layer& layer) {
FloatVector vec;
auto& buffer = layer.params();
auto data = reinterpret_cast<const std::uint16_t*>(buffer.data());
int n = buffer.length() / 2;
vec.resize(n);
for (int i = 0; i < n; i++) {
vec[i] = data[i] / float(0xffff);
vec[i] *= layer.max_val() - layer.min_val();
vec[i] += layer.min_val();
}
return vec;
}

void DenormConvBlock(const pblczero::Weights_ConvBlock& conv, FloatVectors* vecs) {
vecs->emplace_back(DenormLayer(conv.weights()));
vecs->emplace_back(DenormLayer(conv.biases()));
vecs->emplace_back(DenormLayer(conv.bn_means()));
vecs->emplace_back(DenormLayer(conv.bn_stddivs()));
}

} // namespace


FloatVectors LoadFloatsFromPbFile(const std::string& buffer) {
auto net = pblczero::Net();
FloatVectors vecs;
net.ParseFromString(buffer);

std::string min_version(std::to_string(net.min_version().major()) + ".");
min_version += std::to_string(net.min_version().minor()) + ".";
min_version += std::to_string(net.min_version().patch());

if (net.min_version().major() > LC0_VERSION_MAJOR)
throw Exception("Weights require at least lc0 version: " + min_version);
if (net.min_version().minor() > LC0_VERSION_MINOR)
throw Exception("Weights require at least lc0 version: " + min_version);
if (net.min_version().patch() > LC0_VERSION_PATCH)
throw Exception("Weights require at least lc0 version: " + min_version);

if (net.format().weights_encoding() != pblczero::Format::LINEAR16)
throw Exception("Invalid weight encoding");

const auto& w = net.weights();

DenormConvBlock(w.input(), &vecs);

for (int i = 0, n = w.residual_size(); i < n; i++) {
DenormConvBlock(w.residual(i).conv1(), &vecs);
DenormConvBlock(w.residual(i).conv2(), &vecs);
}

DenormConvBlock(w.policy(), &vecs);
vecs.emplace_back(DenormLayer(w.ip_pol_w()));
vecs.emplace_back(DenormLayer(w.ip_pol_b()));
DenormConvBlock(w.value(), &vecs);
vecs.emplace_back(DenormLayer(w.ip1_val_w()));
vecs.emplace_back(DenormLayer(w.ip1_val_b()));
vecs.emplace_back(DenormLayer(w.ip2_val_w()));
vecs.emplace_back(DenormLayer(w.ip2_val_b()));

return vecs;
}

FloatVectors LoadFloatsFromFile(std::string* buffer) {
// Parse buffer.
FloatVectors result;
FloatVector line;
(*buffer) += "\n";
size_t start = 0;
for (size_t i = 0; i < buffer.size(); ++i) {
char& c = buffer[i];
for (size_t i = 0; i < buffer->size(); ++i) {
char& c = (*buffer)[i];
const bool is_newline = (c == '\n' || c == '\r');
if (!std::isspace(c)) continue;
if (start < i) {
// If previous character was not space too.
c = '\0';
line.push_back(std::atof(&buffer[start]));
line.push_back(std::atof(&(*buffer)[start]));
}
if (is_newline && !line.empty()) {
result.emplace_back();
Expand All @@ -73,30 +159,22 @@ FloatVectors LoadFloatsFromFile(const std::string& filename) {
start = i + 1;
}

result.erase(result.begin());
return result;
}

namespace {
void PopulateLastIntoVector(FloatVectors* vecs, Weights::Vec* out) {
*out = std::move(vecs->back());
vecs->pop_back();
}

void PopulateConvBlockWeights(FloatVectors* vecs, Weights::ConvBlock* block) {
PopulateLastIntoVector(vecs, &block->bn_stddivs);
PopulateLastIntoVector(vecs, &block->bn_means);
PopulateLastIntoVector(vecs, &block->biases);
PopulateLastIntoVector(vecs, &block->weights);
}
} // namespace

Weights LoadWeightsFromFile(const std::string& filename) {
FloatVectors vecs = LoadFloatsFromFile(filename);

if (vecs.size() <= 19)
throw Exception("Weights file " + filename +
" should have at least 19 lines");
if (vecs[0][0] != 2) throw Exception("Weights version 2 expected");
FloatVectors vecs;
auto buffer = DecompressGzip(filename);

if (buffer.size() < 2)
throw Exception("Weight file invalid");
else if (buffer[0] == '1' && buffer[1] == '\n')
throw Exception("Weight file no longer supported");
else if (buffer[0] == '2' && buffer[1] == '\n')
vecs = LoadFloatsFromFile(&buffer);
else
vecs = LoadFloatsFromPbFile(buffer);

Weights result;
// Populating backwards.
Expand All @@ -111,10 +189,10 @@ Weights LoadWeightsFromFile(const std::string& filename) {
PopulateConvBlockWeights(&vecs, &result.policy);

// Version, Input + all the residual should be left.
if ((vecs.size() - 5) % 8 != 0)
if ((vecs.size() - 4) % 8 != 0)
throw Exception("Bad number of lines in weights file");

const int num_residual = (vecs.size() - 5) / 8;
const int num_residual = (vecs.size() - 4) / 8;
result.residual.resize(num_residual);
for (int i = num_residual - 1; i >= 0; --i) {
PopulateConvBlockWeights(&vecs, &result.residual[i].conv2);
Expand All @@ -126,13 +204,13 @@ Weights LoadWeightsFromFile(const std::string& filename) {
}

std::string DiscoveryWeightsFile() {
const int kMinFileSize = 10000000; // 10 MB
const int kMinFileSize = 500000; // 500 KB

std::string root_path = CommandLine::BinaryDirectory();

// Open all files in <binary dir> amd <binary dir>/networks,
// ones which are >= kMinFileSize are candidates.
std::vector<std::pair<time_t, std::string>> time_and_filename;
std::vector<std::pair<time_t, std::string> > time_and_filename;
for (const auto& path : {"", "/networks"}) {
for (const auto& file : GetFileList(root_path + path)) {
const std::string filename = root_path + path + "/" + file;
Expand Down
5 changes: 4 additions & 1 deletion src/neural/loader.h
Expand Up @@ -28,8 +28,11 @@ namespace lczero {
using FloatVector = std::vector<float>;
using FloatVectors = std::vector<FloatVector>;

// Read from protobuf.
FloatVectors LoadFloatsFromPbFile(const std::string& buffer);

// Read space separated file of floats and return it as a vector of vectors.
FloatVectors LoadFloatsFromFile(const std::string& filename);
FloatVectors LoadFloatsFromFile(std::string* buffer);

// Read v2 weights file and fill the weights structure.
Weights LoadWeightsFromFile(const std::string& filename);
Expand Down
2 changes: 1 addition & 1 deletion src/version.inc
@@ -1,3 +1,3 @@
#define LC0_VERSION_MAJOR 0
#define LC0_VERSION_MINOR 14
#define LC0_VERSION_MINOR 15

This comment has been minimized.

Copy link
@dubslow

dubslow Jul 16, 2018

Member

This was a major mistake. The rule for this project ought to be that a commit (or PR) either changes only the version, or it changes anything else at all -- never both.

#define LC0_VERSION_PATCH 1
10 changes: 10 additions & 0 deletions subprojects/protobuf.wrap
@@ -0,0 +1,10 @@
[wrap-file]
directory = protobuf-3.5.1

source_url = https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-all-3.5.1.tar.gz
source_filename = protobuf-all-3.5.1.tar.gz
source_hash = 72d43863f58567a9ea2054671fdb667867f9cf7865df623c7be630978ff97dff

patch_url = https://github.com/borg323/protobuf/releases/download/3.5.1-2w/protobuf-3.5.1-2w-wrap.zip
patch_filename = protobuf-3.5.1-2w-wrap.zip
patch_hash = 5185ae7252941e252b075d3f845768296b079516f9f6feb0bd3ae63de7e9a52e

3 comments on commit a530848

@dubslow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Glad to see this critical work make it into main! Promotion inc (never mind faster loading times)!

@Error323
Copy link
Member Author

@Error323 Error323 commented on a530848 Jul 16, 2018 via email

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ScallyBag
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,

Do I need to install something else now? The Raspberry Pi build has worked every time until this latest push.

Thanks,

Al.

Here’s the error log:

Build started at 2018-07-16T11:55:16.497689
Main binary: /usr/bin/python3
Python system: Linux
The Meson build system
Version: 0.46.1
Source dir: /home/Al/lc0
Build dir: /home/Al/lc0/build/release
Build type: native build
Project name: lc0
Sanity testing C++ compiler: c++
Is cross compiler: False.
Sanity check compiler command line: c++ /home/Al/lc0/build/release/meson-private/sanitycheckcpp.cc -o /home/Al/lc0/build/release/meson-private/sanitycheckcpp.exe
Sanity check compile stdout:


Sanity check compile stderr:


Running test binary command: /home/Al/lc0/build/release/meson-private/sanitycheckcpp.exe
Native C++ compiler: c++ (gcc 6.3.0 "c++ (Raspbian 6.3.0-18+rpi1+deb9u1) 6.3.0 20170516")
Build machine cpu family: arm
Build machine cpu: armv7l
Found pkg-config: /usr/bin/pkg-config (0.29)
Determining dependency 'protobuf' with pkg-config executable '/usr/bin/pkg-config'
Using protobuf from cache.
Using protobuf-3.5.1-2w-wrap.zip from cache.

Executing subproject protobuf.

Project name: protobuf
Native C++ compiler: c++ (gcc 6.3.0 "c++ (Raspbian 6.3.0-18+rpi1+deb9u1) 6.3.0 20170516")
Running compile:
Working directory: /tmp/tmpdtwn0zv3
Command line: c++ /tmp/tmpdtwn0zv3/testfile.cpp -pipe -D_FILE_OFFSET_BITS=64 -c -o /tmp/tmpdtwn0zv3/output.obj -O0 -fpermissive -DHAVE_PTHREAD

Code:
int i;

Compiler stdout:

Compiler stderr:

Compiler for C++ supports arguments -DHAVE_PTHREAD: YES
Running compile:
Working directory: /tmp/tmpbszx1ije
Command line: c++ /tmp/tmpbszx1ije/testfile.cpp -pipe -D_FILE_OFFSET_BITS=64 -c -o /tmp/tmpbszx1ije/output.obj -O0 -fpermissive -Wno-sign-compare -Wsign-compare

Code:
int i;

Compiler stdout:

Compiler stderr:

Compiler for C++ supports arguments -Wno-sign-compare -Wsign-compare: YES
Running compile:
Working directory: /tmp/tmpsekrudwt
Command line: c++ /tmp/tmpsekrudwt/testfile.cpp -pipe -D_FILE_OFFSET_BITS=64 -c -o /tmp/tmpsekrudwt/output.obj -O0 -fpermissive -Wno-unused-parameter -Wunused-parameter

Code:
int i;

Compiler stdout:

Compiler stderr:

Compiler for C++ supports arguments -Wno-unused-parameter -Wunused-parameter: YES
Running compile:
Working directory: /tmp/tmpa7hvdlcj
Command line: c++ /tmp/tmpa7hvdlcj/testfile.cpp -pipe -D_FILE_OFFSET_BITS=64 -c -o /tmp/tmpa7hvdlcj/output.obj -O0 -fpermissive -Wno-ignored-qualifiers -Wignored-qualifiers

Code:
int i;

Compiler stdout:

Compiler stderr:

Compiler for C++ supports arguments -Wno-ignored-qualifiers -Wignored-qualifiers: YES
Running compile:
Working directory: /tmp/tmpxicp7jmu
Command line: c++ /tmp/tmpxicp7jmu/testfile.cpp -pipe -D_FILE_OFFSET_BITS=64 -c -o /tmp/tmpxicp7jmu/output.obj -O0 -fpermissive /wd4146

Code:
int i;

Compiler stdout:

Compiler stderr:
c++: error: /wd4146: No such file or directory

Compiler for C++ supports arguments /wd4146: NO
Running compile:
Working directory: /tmp/tmphawir1fw
Command line: c++ /tmp/tmphawir1fw/testfile.cpp -pipe -D_FILE_OFFSET_BITS=64 -c -o /tmp/tmphawir1fw/output.obj -O0 -fpermissive /wd4244

Code:
int i;

Compiler stdout:

Compiler stderr:
c++: error: /wd4244: No such file or directory

Compiler for C++ supports arguments /wd4244: NO
Running compile:
Working directory: /tmp/tmp94kbjw6x
Command line: c++ /tmp/tmp94kbjw6x/testfile.cpp -pipe -D_FILE_OFFSET_BITS=64 -c -o /tmp/tmp94kbjw6x/output.obj -O0 -fpermissive /wd4305

Code:
int i;

Compiler stdout:

Compiler stderr:
c++: error: /wd4305: No such file or directory

Compiler for C++ supports arguments /wd4305: NO
Running compile:
Working directory: /tmp/tmps91un10j
Command line: c++ /tmp/tmps91un10j/testfile.cpp -pipe -D_FILE_OFFSET_BITS=64 -c -o /tmp/tmps91un10j/output.obj -O0 -fpermissive /wd4506

Code:
int i;

Compiler stdout:

Compiler stderr:
c++: error: /wd4506: No such file or directory

Compiler for C++ supports arguments /wd4506: NO
Dependency threads found: YES
Build targets in project: 6

Subproject protobuf finished.
Dependency protobuf from subproject subprojects/protobuf found: YES
Program protoc found: NO
Message: protoc will be built from the subproject

meson.build:52:0: ERROR: File libs/lczero-common/proto/net.proto does not exist.

Please sign in to comment.