prológ
Na internete teraz koluje video, ktoré ukazuje, ako autopilot Tesly vidí cestu.
Už dlho ma núti vysielať video obohatené o detektor a v reálnom čase.
Problém je v tom, že chcem vysielať video z Raspberry a výkon detektora neurónovej siete na ňom ponecháva veľa želaní.
Intel Neural Computer Stick
Zvažoval som rôzne riešenia.
В
Aj keď spoločnosť Intel poskytuje konvertory pre hlavné rámce, existuje niekoľko úskalí.
Napríklad formát požadovanej siete môže byť nekompatibilný, a ak je kompatibilný, niektoré vrstvy nemusia byť na zariadení podporované, a ak sú podporované, môžu sa počas procesu konverzie vyskytnúť chyby, v dôsledku ktorých na výstupe dostaneme nejaké zvláštne veci.
Vo všeobecnosti, ak chcete nejaký druh ľubovoľnej neurónovej siete, potom nemusí fungovať s NCS. Preto som sa rozhodol pokúsiť sa problém vyriešiť pomocou najrozšírenejších a najdostupnejších nástrojov.
mrak
Zjavnou alternatívou k lokálnemu hardvérovému riešeniu je prejsť do cloudu.
Hotové možnosti - oči mi bežia.
Všetci vedúci:
... A desiatky menej známych.
Vybrať si z tejto odrody nie je vôbec jednoduché.
A rozhodol som sa nevybrať, ale zabaliť starú dobrú fungujúcu schému na OpenCV v Dockeri a spustiť ju v cloude.
Výhodou tohto prístupu je flexibilita a kontrola - môžete zmeniť neurónovú sieť, hosting, server - vo všeobecnosti akýkoľvek rozmar.
Server
Začnime s lokálnym prototypom.
Tradične používam Flask pre REST API, OpenCV a MobileSSD sieť.
Po nainštalovaní aktuálnych verzií na Docker som zistil, že OpenCV 4.1.2 nefunguje s Mobile SSD v1_coco_2018_01_28 a musel som sa vrátiť k osvedčenému 11.
Na začiatku služby načítame názvy tried a sieť:
def init():
tf_labels.initLabels(dnn_conf.DNN_LABELS_PATH)
return cv.dnn.readNetFromTensorflow(dnn_conf.DNN_PATH, dnn_conf.DNN_TXT_PATH)
Na miestnom doku (na nie príliš mladom notebooku) to trvá 0.3 sekundy, na Raspberry - 3.5.
Začnime s výpočtom:
def inference(img):
net.setInput(cv.dnn.blobFromImage(img, 1.0/127.5, (300, 300), (127.5, 127.5, 127.5), swapRB=True, crop=False))
return net.forward()
Docker - 0.2 sekundy, Raspberry - 1.7.
Premena výfuku tenzora na čitateľný json:
def build_detection(data, thr, rows, cols):
ret = []
for detection in data[0,0,:,:]:
score = float(detection[2])
if score > thr:
cls = int(detection[1])
a = {"class" : cls, "name" : tf_labels.getLabel(cls), "score" : score}
a["x"] = int(detection[3] * cols)
a["y"] = int(detection[4] * rows)
a["w"] = int(detection[5] * cols ) - a["x"]
a["h"] = int(detection[6] * rows) - a["y"]
ret.append(a)
return ret
ďalej
Alternatívna možnosť, pri ktorej sa viac práce presúva na server: sám zakrúžkuje nájdené objekty a vráti hotový obrázok.
Táto možnosť je dobrá tam, kde nechceme preťahovať opencv na server.
Docker
Zhromažďujeme obrázok.
Kód je vyčesaný a vyvesený
Ako platformu použijeme rovnaký Debian Stretch ako na Raspberry – nevybočíme z osvedčeného tech stacku.
Musíte nainštalovať flask, protobuf, requesty, opencv_python, stiahnuť Mobile SSD, kód servera z Github a spustiť server.
FROM python:3.7-stretch
RUN pip3 install flask
RUN pip3 install protobuf
RUN pip3 install requests
RUN pip3 install opencv_python
ADD http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_11_06_2017.tar.gz /
RUN tar -xvf /ssd_mobilenet_v1_coco_11_06_2017.tar.gz
ADD https://github.com/tprlab/docker-detect/archive/master.zip /
RUN unzip /master.zip
EXPOSE 80
CMD ["python3", "/docker-detect-master/detect-app/app.py"]
prostý
Publikovanie na Docker Hub
Registre Docker sa množia rýchlosťou nie menšou ako cloudové detektory.
Aby sme sa neobťažovali, konzervatívne prejdeme
- Registrovať
- Prihlásiť sa:
prihlásenie do docker - Poďme vymyslieť zmysluplný názov:
docker tag opencv-detect tprlab/opencv-detect-ssd - Nahrajte obrázok na server:
docker push tprlab/opencv-detect-ssd
Spúšťame v cloude
Pomerne široký je aj výber, kam nádobu spustiť.
Všetci veľkí hráči (Google, Microsoft, Amazon) ponúkajú na prvý rok mikroinštanciu zadarmo.
Po experimentovaní s Microsoft Azure a Google Cloud som sa rozhodol pre druhý, pretože sa to rozbehlo rýchlejšie.
Návod som tu nenapísal, keďže táto časť je veľmi špecifická pre vybraného poskytovateľa.
Skúšal som rôzne možnosti hardvéru,
Nízke úrovne (zdieľané a vyhradené) - 0.4 - 0.5 sekundy.
Silnejšie autá - 0.25 - 0.3.
No aj v najhoršom prípade sú výhry trojnásobné, môžete skúsiť.
video
Na Raspberry spúšťame jednoduchý streamer videa OpenCV, ktorý zisťujeme prostredníctvom Google Cloud.
Na experiment bol použitý video súbor, ktorý bol raz natočený na náhodnej križovatke.
def handle_frame(frame):
return detect.detect_draw_img(frame)
def generate():
while True:
rc, frame = vs.read()
outFrame = handle_frame(frame)
if outFrame is None:
(rc, outFrame) = cv.imencode(".jpg", frame)
yield(b'--framern' b'Content-Type: image/jpegrnrn' + bytearray(outFrame) + b'rn')
@app.route("/stream")
def video_feed():
return Response(generate(), mimetype = "multipart/x-mixed-replace; boundary=frame")
S detektorom nedostávame viac ako tri snímky za sekundu, všetko ide veľmi pomaly.
Ak do GCloud vezmete výkonný stroj, môžete rozpoznať 4-5 snímok za sekundu, ale rozdiel je takmer neviditeľný pre oči, stále je pomalý.
Cloud a náklady na dopravu s tým nemajú nič spoločné, detektor beží na bežnom hardvéri a pracuje takou rýchlosťou.
Neurónová počítačová tyčinka
Nemohol som odolať a spustil som benchmark na NCS.
Rýchlosť detektora bola o niečo nižšia ako 0.1 sekundy, v každom prípade 2-3 krát rýchlejšia ako cloud na slabom stroji, t.j. 8-9 snímok za sekundu.
Rozdiel vo výsledkoch je vysvetlený skutočnosťou, že NCS používalo verziu Mobile SSD 2018_01_28.
P.S. Okrem toho experimenty ukázali, že pomerne výkonný stolný stroj s procesorom I7 vykazuje o niečo lepšie výsledky a ukázalo sa, že je možné vytlačiť 10 snímok za sekundu.
zhluk
Experiment išiel ďalej a detektor som nainštaloval na päť uzlov v Google Kubernetes.
Samotné moduly boli slabé a každý z nich nedokázal spracovať viac ako 2 snímky za sekundu.
Ale ak spustíte klaster s N uzlami a analyzujete snímky v N vláknach, potom s dostatočným počtom uzlov (5) môžete dosiahnuť požadovaných 10 snímok za sekundu.
def generate():
while True:
rc, frame = vs.read()
if frame is not None:
future = executor.submit(handle_frame, (frame.copy()))
Q.append(future)
keep_polling = len(Q) > 0
while(keep_polling):
top = Q[0]
if top.done():
outFrame = top.result()
Q.popleft()
if outFrame:
yield(b'--framern' b'Content-Type: image/jpegrnrn' + bytearray(outFrame) + b'rn')
keep_polling = len(Q) > 0
else:
keep_polling = len(Q) >= M
Tu sa stalo:
Trochu menej rýchle ako s NCS, ale razantnejšie ako v jednom prúde.
Zisk, samozrejme, nie je lineárny - existujú prekrytia pre synchronizáciu a hlboké kopírovanie obrázkov opencv.
Záver
Celkovo nám experiment umožňuje dospieť k záveru, že ak sa pokúsite, môžete sa dostať preč s jednoduchým cloudom.
Ale výkonný desktop alebo lokálny hardvér vám umožní dosiahnuť lepšie výsledky a to bez akýchkoľvek trikov.
referencie
Zdroj: hab.com