2017-12-13 90 views
11

मेरे पास एकाधिक जीपीयू वाले सर्वर हैं और जावा ऐप के अंदर मॉडल अनुमान के दौरान उनका पूरा उपयोग करना चाहते हैं। डिफ़ॉल्ट रूप से tensorflow सभी उपलब्ध GPUs को जब्त करता है, लेकिन केवल पहले का उपयोग करता है।टेन्सफोर्लो जावा मल्टी-जीपीयू अनुमान

मैं तीन विकल्प के बारे में सोच सकते हैं इस मुद्दे पर काबू पाने के लिए:

  1. प्रक्रिया स्तर पर डिवाइस दृश्यता सीमित करें, अर्थात् CUDA_VISIBLE_DEVICES वातावरण चर का उपयोग कर।

    मुझे जावा ऐप के कई उदाहरण चलाने और उनके बीच यातायात वितरित करने की आवश्यकता होगी। वह मोहक विचार नहीं है। एक भी आवेदन के अंदर

  2. लॉन्च कई सत्रों और ConfigProto के माध्यम से उनमें से प्रत्येक को एक डिवाइस आवंटित करने के लिए प्रयास करें:

    public class DistributedPredictor { 
    
        private Predictor[] nested; 
        private int[] counters; 
    
        // ... 
    
        public DistributedPredictor(String modelPath, int numDevices, int numThreadsPerDevice) { 
         nested = new Predictor[numDevices]; 
         counters = new int[numDevices]; 
    
         for (int i = 0; i < nested.length; i++) { 
          nested[i] = new Predictor(modelPath, i, numDevices, numThreadsPerDevice); 
         } 
        } 
    
        public Prediction predict(Data data) { 
         int i = acquirePredictorIndex(); 
         Prediction result = nested[i].predict(data); 
         releasePredictorIndex(i); 
         return result; 
        } 
    
        private synchronized int acquirePredictorIndex() { 
         int i = argmin(counters); 
         counters[i] += 1; 
         return i; 
        } 
    
        private synchronized void releasePredictorIndex(int i) { 
         counters[i] -= 1; 
        } 
    } 
    
    
    public class Predictor { 
    
        private Session session; 
    
        public Predictor(String modelPath, int deviceIdx, int numDevices, int numThreadsPerDevice) { 
    
         GPUOptions gpuOptions = GPUOptions.newBuilder() 
           .setVisibleDeviceList("" + deviceIdx) 
           .setAllowGrowth(true) 
           .build(); 
    
         ConfigProto config = ConfigProto.newBuilder() 
           .setGpuOptions(gpuOptions) 
           .setInterOpParallelismThreads(numDevices * numThreadsPerDevice) 
           .build(); 
    
         byte[] graphDef = Files.readAllBytes(Paths.get(modelPath)); 
         Graph graph = new Graph(); 
         graph.importGraphDef(graphDef); 
    
         this.session = new Session(graph, config.toByteArray()); 
        } 
    
        public Prediction predict(Data data) { 
         // ... 
        } 
    } 
    

    यह दृष्टिकोण एक नज़र में ठीक से काम करने लगता है। हालांकि, सत्र कभी-कभी setVisibleDeviceList विकल्प को अनदेखा करते हैं और सभी आउट-ऑफ-मेमोरी क्रैश के कारण पहले डिवाइस के लिए जाते हैं।

  3. tf.device() विनिर्देश का उपयोग कर पाइथन में एक बहु-टावर फैशन में मॉडल बनाएं। जावा पक्ष पर, साझा सत्र के अंदर अलग-अलग Predictor के विभिन्न टावर दें।

    मेरे लिए बोझिल और मूर्खतापूर्ण रूप से गलत लगता है।

अद्यतन: @ash प्रस्तावित हैं, वहाँ अभी तक एक और विकल्प है:

  1. इसकी परिभाषा को संशोधित करके मौजूदा ग्राफ में से प्रत्येक के संचालन के लिए एक उपयुक्त उपकरण असाइन करें (graphDef)।

    यह कर पाने के लिए, एक विधि 2 से कोड अनुकूलन कर सकते हैं:

    public class Predictor { 
    
        private Session session; 
    
        public Predictor(String modelPath, int deviceIdx, int numDevices, int numThreadsPerDevice) { 
    
         byte[] graphDef = Files.readAllBytes(Paths.get(modelPath)); 
         graphDef = setGraphDefDevice(graphDef, deviceIdx) 
    
         Graph graph = new Graph(); 
         graph.importGraphDef(graphDef); 
    
         ConfigProto config = ConfigProto.newBuilder() 
           .setAllowSoftPlacement(true) 
           .build(); 
    
         this.session = new Session(graph, config.toByteArray()); 
        } 
    
        private static byte[] setGraphDefDevice(byte[] graphDef, int deviceIdx) throws InvalidProtocolBufferException { 
         String deviceString = String.format("/gpu:%d", deviceIdx); 
    
         GraphDef.Builder builder = GraphDef.parseFrom(graphDef).toBuilder(); 
         for (int i = 0; i < builder.getNodeCount(); i++) { 
          builder.getNodeBuilder(i).setDevice(deviceString); 
         } 
         return builder.build().toByteArray(); 
        } 
    
        public Prediction predict(Data data) { 
         // ... 
        } 
    } 
    

    बस अन्य उल्लेख दृष्टिकोण की तरह, यह भी मुझे मैन्युअल उपकरणों के बीच डेटा का वितरण कर से मुक्त नहीं है। लेकिन कम से कम यह स्थिरता से काम करता है और लागू करने के लिए तुलनात्मक रूप से आसान है। कुल मिलाकर, यह एक (लगभग) सामान्य तकनीक की तरह दिखता है।

क्या tensorflow जावा एपीआई के साथ ऐसी मूलभूत चीज़ करने का एक शानदार तरीका है? किसी भी सुझाव की सराहना की जाएगी।

उत्तर

6

संक्षेप में: एक कामकाज है, जहां आप प्रति GPU एक सत्र के साथ समाप्त होते हैं।

विवरण:

सामान्य प्रवाह कि TensorFlow क्रम उपकरणों ग्राफ में परिचालन के लिए निर्दिष्ट का सम्मान करता है। यदि किसी ऑपरेशन के लिए कोई डिवाइस निर्दिष्ट नहीं है, तो यह कुछ हेरिस्टिक्स पर आधारित "स्थान" करता है। वर्तमान में उन हेरिस्टिक्स का परिणाम "GPU पर स्थान ऑपरेशन: 0 यदि GPU उपलब्ध हैं और ऑपरेशन के लिए एक GPU कर्नेल है" (Placer::Run यदि आप रुचि रखते हैं)।

जो भी आप पूछते हैं मुझे लगता है कि टेंसरफ्लो के लिए एक उचित फीचर अनुरोध है - धारावाहिक ग्राफ में डिवाइसों को "आभासी" के रूप में चलाने के लिए "फाईस्कल" उपकरणों के सेट पर मैप किए जाने की क्षमता, या वैकल्पिक रूप से सेटिंग "डिफ़ॉल्ट डिवाइस"। यह सुविधा वर्तमान में मौजूद नहीं है। ConfigProto पर ऐसा विकल्प जोड़ना कुछ ऐसा है जो आप एक फीचर अनुरोध दर्ज करना चाहते हैं।

मैं अंतरिम में एक समाधान का सुझाव दे सकता हूं। सबसे पहले, आपके प्रस्तावित समाधानों पर कुछ टिप्पणी।

  1. आपका पहला विचार निश्चित रूप से काम करेगा, लेकिन जैसा कि आपने बताया है, बोझिल है।

  2. ConfigProto में visible_device_list का उपयोग कर काफी बाहर काम नहीं करता है के बाद से है कि वास्तव में एक प्रति प्रक्रिया सेटिंग है और बाद पहले सत्र प्रक्रिया में बनाई गई है नजरअंदाज कर दिया है की स्थापना। यह निश्चित रूप से दस्तावेज नहीं है और साथ ही यह होना चाहिए (और कुछ हद तक दुर्भाग्यपूर्ण है कि यह प्रति सत्र कॉन्फ़िगरेशन में दिखाई देता है)। हालांकि, यह बताता है कि यहां आपका सुझाव क्यों काम नहीं करता है और आप अभी भी एक जीपीयू क्यों इस्तेमाल करते हैं।

  3. यह काम कर सकता है।

एक अन्य विकल्प विभिन्न ग्राफ़ के साथ समाप्त करने के लिए (के साथ संचालन स्पष्ट रूप से अलग GPUs पर रखा), GPU के प्रति एक सत्र में जिसके परिणामस्वरूप है।

final int NUM_GPUS = 8; 
// setAllowSoftPlacement: Just in case our device modifications were too aggressive 
// (e.g., setting a GPU device on an operation that only has CPU kernels) 
// setLogDevicePlacment: So we can see what happens. 
byte[] config = 
    ConfigProto.newBuilder() 
     .setLogDevicePlacement(true) 
     .setAllowSoftPlacement(true) 
     .build() 
     .toByteArray(); 
Graph graphs[] = new Graph[NUM_GPUS]; 
Session sessions[] = new Session[NUM_GPUS]; 
for (int i = 0; i < NUM_GPUS; ++i) { 
    graphs[i] = new Graph(); 
    graphs[i].importGraphDef(modifyGraphDef(graphDef, String.format("/gpu:%d", i))); 
    sessions[i] = new Session(graphs[i], config);  
} 

तब का उपयोग करें:

public static byte[] modifyGraphDef(byte[] graphDef, String device) throws Exception { 
    GraphDef.Builder builder = GraphDef.parseFrom(graphDef).toBuilder(); 
    for (int i = 0; i < builder.getNodeCount(); ++i) { 
    builder.getNodeBuilder(i).setDevice(device); 
    } 
    return builder.build().toByteArray(); 
} 

जो बाद आप की तरह कुछ का उपयोग कर एक Graph और Session GPU प्रति बना सकते हैं: कुछ इस तरह ग्राफ को संपादित करने और स्पष्ट रूप से प्रत्येक ऑपरेशन के लिए एक उपकरण आवंटित करने के लिए इस्तेमाल किया जा सकता GPU #i पर ग्राफ निष्पादित करने के लिए sessions[i]

उम्मीद है कि मदद करता है।

संबंधित मुद्दे