页面树结构

2017-11-09 ApacheCN 开源组织,第二期邀请成员活动,一起走的更远 : http://www.apachecn.org/member/209.html


MachineLearning 优酷地址 : http://i.youku.com/apachecn

转至元数据结尾
转至元数据起始

背景

TensorFlow框架通常用于多进程和多机器环境,例如Google数据中心,Google云机器学习,Amazon Web Services(AWS)和现场分布式集群。为了共享和保存由TensorFlow生成的某些类型的状态,该框架假定存在可靠的共享文件系统。这个共享文件系统有很多用途,例如:

  • 状态检查点经常保存到分布式文件系统中,以实现可靠性和容错性。
  • 培训过程通过将事件文件写入TensorBoard监视的目录与TensorBoard进行通信。共享文件系统允许这种通信工作,即使TensorBoard运行在不同的进程或机器。

在现实世界中有许多不同的共享或分布式文件系统实现,所以TensorFlow为用户提供了一个可以在TensorFlow运行时注册的自定义FileSystem插件的功能。当TensorFlow运行时尝试通过FileSystem 接口写入文件时,它使用路径名的一部分动态选择应用于文件系统操作的实现。因此,添加对自定义文件系统的支持需要实现一个FileSystem 接口,构建包含该实现的共享对象,并在运行时加载该对象,无论哪个进程需要写入该文件系统。

请注意,TensorFlow已经包含许多文件系统实现,例如:

  • 标准的POSIX文件系统

    • 注意:NFS文件系统通常作为POSIX接口安装,因此标准的TensorFlow可以在NFS安装的远程文件系统之上工作。 HDFS - Hadoop文件系统 GCS - Google云存储文件系统 *一个“内存映射文件”文件系统

本指南的其余部分介绍如何实现自定义文件系统。


实现自定义文件系统插件

要实现自定义文件系统插件,您必须执行以下操作:

  • 实现的子类RandomAccessFileWriteableFile, AppendableFile,和ReadOnlyMemoryRegion
  • 实现FileSystem接口作为子类。
  •  FileSystem使用适当的前缀模式注册实现。
  • 在要写入该文件系统的进程中加载文件系统插件。

FileSystem界面

FileSystem接口是一个在file_system.h中定义的抽象C ++ 接口。接口的FileSystem实现应该实现接口定义的所有相关方法。实现该接口要求限定,例如,创建动作RandomAccessFileWritableFile和实施标准的文件系统操作,例如FileExistsIsDirectory, GetMatchingPathsDeleteFile,等。这些接口的实现通常包括将函数的输入参数转换为已经存在的库函数,以实现自定义文件系统中的等效功能。

例如,PosixFileSystem实现DeleteFile使用POSIX unlink()函数实现; CreateDir简单地调用mkdir()GetFileSize 涉及到调用stat()该文件,然后返回文件大小,返回stat对象。类似地,为了HDFSFileSystem 实现,这些调用简单地委托给libHDFS类似功能的实现,例如hdfsDelete对于DeleteFile

我们建议您查看这些代码示例来了解不同文件系统实现如何调用其现有库。示例包括:

文件界面

除此之外操作,它允许您查询和操作文件系统中的文件和目录,该FileSystem接口需要您实现返回抽象对象如的实现工厂 的RandomAccessFileWritableFile,让TensorFlow代码,并读取和写入该文件FileSystem执行。

要实现一个RandomAccessFile,您必须实现一个名为 Read()的接口,其中实现必须提供一种从命名文件中的偏移量读取的方法。

例如,下面是用于POSIX文件系统的RandomAccessFile的实现,它使用pread()随机访问POSIX函数实现读取。请注意,特定实现必须知道如何从底层文件系统重试或传播错误。

 

    class PosixRandomAccessFile : public RandomAccessFile {
     public:
      PosixRandomAccessFile(const string& fname, int fd)
          : filename_(fname), fd_(fd) {}
      ~PosixRandomAccessFile() override { close(fd_); }

      Status Read(uint64 offset, size_t n, StringPiece* result,
                  char* scratch) const override {
        Status s;
        char* dst = scratch;
        while (n > 0 && s.ok()) {
          ssize_t r = pread(fd_, dst, n, static_cast<off_t>(offset));
          if (r > 0) {
            dst += r;
            n -= r;
            offset += r;
          } else if (r == 0) {
            s = Status(error::OUT_OF_RANGE, "Read less bytes than requested");
          } else if (errno == EINTR || errno == EAGAIN) {
            // Retry
          } else {
            s = IOError(filename_, errno);
          }
        }
        *result = StringPiece(scratch, dst - scratch);
        return s;
      }

     private:
      string filename_;
      int fd_;
    }; 
为了实现WritableFile顺序书写的抽象,一个必须实现的几个接口,如Append()Flush()Sync(),和Close()

例如,下面是用于POSIX文件系统的WritableFile的实现,该文件系统FILE在其构造函数中接受一个对象,并在该对象上使用标准的posix函数来实现该接口。

    class PosixWritableFile : public WritableFile {
     public:
      PosixWritableFile(const string& fname, FILE* f)
          : filename_(fname), file_(f) {}

      ~PosixWritableFile() override {
        if (file_ != NULL) {
          fclose(file_);
        }
      }

      Status Append(const StringPiece& data) override {
        size_t r = fwrite(data.data(), 1, data.size(), file_);
        if (r != data.size()) {
          return IOError(filename_, errno);
        }
        return Status::OK();
      }

      Status Close() override {
        Status result;
        if (fclose(file_) != 0) {
          result = IOError(filename_, errno);
        }
        file_ = NULL;
        return result;
      }

      Status Flush() override {
        if (fflush(file_) != 0) {
          return IOError(filename_, errno);
        }
        return Status::OK();
      }

      Status Sync() override {
        Status s;
        if (fflush(file_) != 0) {
          s = IOError(filename_, errno);
        }
        return s;
      }

     private:
      string filename_;
      FILE* file_;
    };
有关更多详细信息,请参阅这些界面的文档,并查看示例实现的灵感。

注册和加载文件系统

一旦您实现了FileSystem自定义文件系统的实现,您需要在“方案”下注册它,以便以该方案为前缀的路径指向您的实现。要做到这一点,你可以调用 REGISTER_FILE_SYSTEM::

REGISTER_FILE_SYSTEM("foobar", FooBarFileSystem);
当TensorFlow尝试对其路径开始的文件进行操作时foobar://,将使用该FooBarFileSystem实现。
string filename = "foobar://path/to/file.txt";
 std::unique_ptr<WritableFile> file;

 // Calls FooBarFileSystem::NewWritableFile to return
 // a WritableFile class, which happens to be the FooBarFileSystem's
 // WritableFile implementation.
 TF_RETURN_IF_ERROR(env->NewWritableFile(filename, &file));
接下来,您必须构建包含此实现的共享对象。使用bazel cc_binary规则执行此操作的示例可以在这里找到 ,但您可以使用任何构建系统来执行此操作。有关类似的说明,请参阅构建op库的部分。

构建此目标的结果是.so共享对象文件。

最后,您必须在该过程中动态加载此实现。在Python中,您可以调用该tf.load_file_system_library(file_system_library)函数,将该路径传递给共享对象。在客户机程序中调用此过程会加载该进程中的共享对象,从而将您的实现注册为可用于通过该FileSystem接口的任何文件操作。你可以看到 一个例子的test_file_system.py

通过这个界面?

内几乎所有TensorFlow核心C ++文件操作使用FileSystem 界面,比如CheckpointWriter,在EventsWriter和许多其他工具。这意味着实现一个FileSystem实现允许您的大部分TensorFlow程序写入您的共享文件系统。

在Python中,该类gfilefile_io类通过SWIG绑定到“FileSystem”实现,这意味着一旦加载了该文件系统库,就可以执行以下操作:

 

with gfile.Open("foobar://path/to/file.txt") as w:

 w.write("hi")
执行此操作时,包含“hi”的文件将显示在共享文件系统的“/path/to/file.txt”中。

  • 无标签