Wir bewerten russische Städte nach Straßenqualität



Noch einmal, als ich mit dem Auto durch meine Heimatstadt fuhr und eine andere Grube umrundete, dachte ich: Gab es überall in unserem Land so „gute“ Straßen und ich entschied, dass wir die Situation objektiv anhand der Straßenqualität in unserem Land bewerten sollten.

Aufgabenformalisierung


In Russland sind die Anforderungen an die Straßenqualität in GOST R 50597-2017 „Straßen und Wege. Anforderungen an den Betriebszustand, die unter den Bedingungen der Gewährleistung der Verkehrssicherheit akzeptabel sind. Kontrollmethoden. " In diesem Dokument werden die Anforderungen für die Abdeckung der Fahrbahn, der Straßenränder, der Trennstreifen, der Bürgersteige, der Fußgängerwege usw. definiert und die Arten von Schäden festgelegt.

Da die Ermittlung aller Parameter der Straßen sehr umfangreich ist, habe ich mich entschlossen, sie einzugrenzen und mich nur auf das Problem der Ermittlung von Fehlern in der Fahrbahnabdeckung zu konzentrieren. In GOST R 50597-2017 werden folgende Mängel in der Fahrbahnbeschichtung unterschieden:

  • Schlaglöcher
  • bricht
  • Drawdowns
  • verschiebt sich
  • Kämme
  • verfolgen
  • schwitzender Binder

Ich habe mich entschlossen, diese Mängel zu beheben.

Datenerfassung


Wo kann ich Fotos bekommen, die ausreichend große Abschnitte der Fahrbahn und sogar in Bezug auf die Geolokalisierung darstellen? Die Antwort kam in Strasssteinen - Panoramen auf den Karten von Yandex (oder Google), aber nach einigem Suchen fand ich mehrere weitere alternative Optionen:

  • Ausgabe von Suchmaschinen für Bilder für relevante Anfragen;
  • Fotos auf Websites für den Empfang von Beschwerden (Rosyama, Angry Citizen, Tugend usw.)
  • Opendatascience veranlasste ein Projekt, Straßenfehler mit einem markierten Datensatz zu erkennen - github.com/sekilab/RoadDamageDetector

Leider hat eine Analyse dieser Optionen ergeben, dass sie für mich nicht sehr geeignet sind: Die Ausgabe von Suchmaschinen ist sehr laut (viele Fotos sind keine Straßen, verschiedene Renderings usw.), Fotos von Websites, auf denen Beschwerden eingehen, enthalten nur Fotos mit großen Verstößen gegen die Asphaltoberfläche Es gibt einige Fotos mit kleinen Verstößen gegen die Abdeckung und ohne Verstöße auf diesen Websites. Der Datensatz aus dem RoadDamageDetector-Projekt wird in Japan gesammelt und enthält keine Beispiele mit großen Verstößen gegen die Abdeckung sowie Straßen ohne Abdeckung.

Da alternative Optionen nicht geeignet sind, verwenden wir Yandex-Panoramen (ich habe die Google-Panoramaoption ausgeschlossen, da der Dienst in weniger Städten in Russland angeboten und weniger häufig aktualisiert wird). Er beschloss, Daten in Städten mit mehr als 100.000 Einwohnern sowie in Bundeszentren zu sammeln. Ich habe eine Liste mit Städtenamen erstellt - es gab 176, später stellte sich heraus, dass nur 149 von ihnen Panoramen haben. Ich werde mich nicht mit den Funktionen des Parsens von Kacheln befassen. Ich werde sagen, dass ich am Ende 149 Ordner (einen für jede Stadt) erhalten habe, in denen sich insgesamt 1,7 Millionen Fotos befanden. Für Novokuznetsk sah der Ordner beispielsweise folgendermaßen aus:



Durch die Anzahl der heruntergeladenen Fotos wurden die Städte wie folgt verteilt:

Tabelle
Stadt
Anzahl der Fotos, Stk
Moskau

86048

Sankt Petersburg

41376

Saransk

18880

Podolsk

18560

Krasnogorsk

18208

Lyubertsie

17760

Kaliningrad

16928

Kolomna

16832

Mytishchi

16192

Wladiwostok

16096

Balashikha

15968

Petrosawodsk

15968

Jekaterinburg

15808

Veliky Novgorod

15744

Naberezhnye Chelny

15680

Krasnodar

15520

Nizhny Novgorod

15488

Khimki

15296

Tula

15296

Nowosibirsk

15264

Tver

15200

Miass

15104

Ivanovo

15072

Vologda

15008

Schukowski

14976

Kostroma

14912

Samara

14880

Korolev

14784

Kaluga

14720

Cherepovets

14720

Sewastopol

14688

Pushkino

14528

Jaroslawl

14464

Uljanowsk

14400

Rostow am Don

14368

Domodedovo

14304

Kamensk-Uralsky

14208

Pskov

14144

Yoshkar-Ola

14080

Kertsch

14080

Murmansk

13920

Togliatti

13920

Wladimir

13792

Adler

13792

Syktyvkar

13728

Dolgoprudny

13696

Khanty-Mansiysk

13664

Kasan

13600

Engels

13440

Archangelsk

13280

Brjansk

13216

Omsk

13120

Syzran

13088

Krasnojarsk

13056

Shchelkovo

12928

Penza

12864

Tscheljabinsk

12768

Cheboksary

12768

Nischni Tagil

12672

Stavropol

12672

Ramenskoye

12640

Irkutsk

12608

Angarsk

12608

Tjumen

12512

Odintsovo

12512

Ufa

12512

Magadan

12512

Perm

12448

Kirov

12256

Nischnekamsk

12224

Makhachkala

12096

Nischnewartowsk

11936

Kursk

11904

Sotschi

11872

Tambow

11840

Pjatigorsk

11808

Wolgodonsk

11712

Rjasan

11680

Saratow

11616

Dzerzhinsk

11456

Orenburg

11456

Hügel

11424

Wolgograd

11264

Ischewsk

11168

Chrysostomus

11136

Lipetsk

11072

Kislowodsk

11072

Surgut

11040

Magnitogorsk

10912

Smolensk

10784

Chabarowsk

10752

Kopeysk

10688

Maykop

10656

Petropawlowsk-Kamtschatski

10624

Taganrog

10560

Barnaul

10528

Sergiev Posad

10368

Elista

10304

Sterlitamak

9920

Simferopol

9824

Tomsk

9760

Orekhovo-Zuevo

9728

Astrachan

9664

Evpatoria

9568

Noginsk

9344

Chita

9216

Belgorod

9120

Biysk

8928

Rybinsk

8896

Sewerodwinsk

8832

Woronesch

8768

Blagoveshchensk

8672

Novorossiysk

8608

Ulan-Ude

8576

Serpukhov

8320

Komsomolsk-on-Amur

8192

Abakan

8128

Norilsk

8096

Juschno-Sachalininsk

8032

Obninsk

7904

Essentuki

7712

Bataysk

7648

Wolzhsky

7584

Novocherkassk

7488

Berdsk

7456

Arzamas

7424

Pervouralsk

7392

Kemerowo

7104

Elektrostal

6720

Derbent

6592

Jakutsk

6528

Murom

6240

Nefteyugansk

5792

Reutov

5696

Birobidschan

5440

Novokuybyshevsk

5248

Salekhard

5184

Nowokusnezk

5152

Novy Urengoy

4736

Noyabrsk

4416

Novocheboksarsk

4352

Yelets

3968

Kaspiysk

3936

Stary Oskol

3840

Artyom

3744

Zheleznogorsk

3584

Salavat

3584

Prokopyevsk

2816

Gorno-Altaysk

2464



Vorbereiten eines Datensatzes für das Training


Und so wird der Datensatz zusammengestellt. Wie können Sie nun anhand eines Fotos des Straßenabschnitts und der umgebenden Objekte die Qualität des darauf abgebildeten Asphalts herausfinden? Ich beschloss, ein Stück des Fotos mit einer Größe von 350 * 244 Pixel in der Mitte des Originalfotos direkt unterhalb der Mitte auszuschneiden. Reduzieren Sie dann das geschnittene Stück horizontal auf eine Größe von 244 Pixel. Das resultierende Bild (244 * 244 groß) ist die Eingabe für den Faltungscodierer:



Um besser zu verstehen, mit welchen Daten ich zu tun habe, wurden die ersten 2000 Bilder, die ich selbst markiert habe, und die restlichen Bilder von Yandex.Tolki-Mitarbeitern markiert. Vor ihnen habe ich im folgenden Wortlaut eine Frage gestellt.

Geben Sie an, welche Straßenoberfläche Sie auf dem Foto sehen:

  1. Boden / Schutt
  2. Pflastersteine, Fliesen, Pflaster
  3. Schienen, Eisenbahnschienen
  4. Wasser, große Pfützen
  5. Asphalt
  6. Es gibt keine Straße auf dem Foto / Fremdkörper / Die Abdeckung ist aufgrund von Autos nicht sichtbar

Wenn der Darsteller „Asphalt“ wählte, erschien ein Menü, in dem die Qualität bewertet werden konnte:

  1. Hervorragende Abdeckung
  2. Leichte Einzelrisse / flache Schlaglöcher
  3. Große Risse / Gitterrisse / einzelne kleine Schlaglöcher
  4. Große Schlaglöcher / tiefe Schlaglöcher / zerstörte Beschichtung

Wie Testläufe der Aufgaben zeigten, unterscheiden sich die Darsteller von Y. Toloki nicht in der Integrität der Arbeit - sie klicken versehentlich mit der Maus auf die Felder und betrachten die Aufgabe als erledigt. Ich musste Kontrollfragen hinzufügen (in der Aufgabe gab es 46 Fotos, von denen 12 Kontrollfragen waren) und eine verzögerte Annahme ermöglichen. Als Kontrollfragen habe ich die Bilder verwendet, die ich selbst markiert habe. Ich habe die verzögerte Annahme automatisiert. Mit Y. Toloka können Sie die Arbeitsergebnisse in eine CSV-Datei hochladen und die Ergebnisse der Überprüfung der Antworten laden. Die Überprüfung der Antworten funktionierte wie folgt: Wenn die Aufgabe mehr als 5% falsche Antworten auf Kontrollfragen enthält, gilt sie als unerfüllt. Wenn der Auftragnehmer eine Antwort angibt, die logisch nahe an der Wahrheit liegt, wird seine Antwort als richtig angesehen.
Als Ergebnis erhielt ich ungefähr 30.000 getaggte Fotos, die ich für das Training in drei Klassen verteilte:

  • "Gut" - Fotos mit den Bezeichnungen "Asphalt: Ausgezeichnete Beschichtung" und "Asphalt: Kleinere Einzelrisse"
  • "Mitte" - Fotos mit den Bezeichnungen "Pflastersteine, Fliesen, Pflaster", "Schienen, Eisenbahnschienen" und "Asphalt: Große Risse / Gitterrisse / einzelne kleine Schlaglöcher"
  • "Groß" - Fotos mit den Bezeichnungen "Boden / Schotter", "Wasser, große Pfützen" und "Asphalt: Eine große Anzahl von Schlaglöchern / tiefen Schlaglöchern / zerstörten Gehwegen"
  • Fotos mit dem Tag „Es gibt keine Straße auf dem Foto / Fremdkörper / Abdeckung ist aufgrund von Autos nicht sichtbar“ gab es nur sehr wenige (22 Stück). Und ich habe sie von weiteren Arbeiten ausgeschlossen

Entwicklung und Schulung von Klassifikatoren


Also, die Daten werden gesammelt und beschriftet, fahren wir mit der Entwicklung des Klassifikators fort. Normalerweise wird für die Aufgaben der Bildklassifizierung, insbesondere beim Training mit kleinen Datensätzen, ein vorgefertigter Faltungscodierer verwendet, an dessen Ausgabe ein neuer Klassifizierer angeschlossen wird. Ich entschied mich für einen einfachen Klassifikator ohne versteckte Ebene, eine Eingabeebene der Größe 128 und eine Ausgabeebene der Größe 3. Ich entschied mich, sofort mehrere vorgefertigte Optionen zu verwenden, die in ImageNet als Encoder trainiert wurden:

  • Xception
  • Resnet
  • Inception
  • Vgg16
  • Densenet121
  • Mobilenet

Hier ist die Funktion, die das Keras-Modell mit dem angegebenen Encoder erstellt:

def createModel(typeModel): conv_base = None if(typeModel == "nasnet"): conv_base = keras.applications.nasnet.NASNetMobile(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "xception"): conv_base = keras.applications.xception.Xception(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "resnet"): conv_base = keras.applications.resnet50.ResNet50(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "inception"): conv_base = keras.applications.inception_v3.InceptionV3(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "densenet121"): conv_base = keras.applications.densenet.DenseNet121(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "mobilenet"): conv_base = keras.applications.mobilenet_v2.MobileNetV2(include_top=False, input_shape=(224,224,3), weights='imagenet') if(typeModel == "vgg16"): conv_base = keras.applications.vgg16.VGG16(include_top=False, input_shape=(224,224,3), weights='imagenet') conv_base.trainable = False model = Sequential() model.add(conv_base) model.add(Flatten()) model.add(Dense(128, activation='relu', kernel_regularizer=regularizers.l2(0.0002))) model.add(Dropout(0.3)) model.add(Dense(3, activation='softmax')) model.compile(optimizer=keras.optimizers.Adam(lr=1e-4), loss='binary_crossentropy', metrics=['accuracy']) return model 

Für das Training habe ich einen Generator mit Augmentation verwendet (da mir die Möglichkeiten der in Keras integrierten Augmentation unzureichend erschienen, habe ich die Augmentor- Bibliothek verwendet):

  • Pisten
  • Zufällige Verzerrung
  • Dreht sich
  • Farbtausch
  • Schichten
  • Ändern Sie Kontrast und Helligkeit
  • Zufälliges Rauschen hinzufügen
  • Ernte

Nach der Vergrößerung sahen die Fotos folgendermaßen aus:



Generatorcode:

 def get_datagen(): train_dir='~/data/train_img' test_dir='~/data/test_img' testDataGen = ImageDataGenerator(rescale=1. / 255) train_generator = datagen.flow_from_directory( train_dir, target_size=img_size, batch_size=16, class_mode='categorical') p = Augmentor.Pipeline(train_dir) p.skew(probability=0.9) p.random_distortion(probability=0.9,grid_width=3,grid_height=3,magnitude=8) p.rotate(probability=0.9, max_left_rotation=5, max_right_rotation=5) p.random_color(probability=0.7, min_factor=0.8, max_factor=1) p.flip_left_right(probability=0.7) p.random_brightness(probability=0.7, min_factor=0.8, max_factor=1.2) p.random_contrast(probability=0.5, min_factor=0.9, max_factor=1) p.random_erasing(probability=1,rectangle_area=0.2) p.crop_by_size(probability=1, width=244, height=244, centre=True) train_generator = keras_generator(p,batch_size=16) test_generator = testDataGen.flow_from_directory( test_dir, target_size=img_size, batch_size=32, class_mode='categorical') return (train_generator, test_generator) 

Der Code zeigt, dass die Erweiterung nicht für Testdaten verwendet wird.

Wenn Sie einen abgestimmten Generator haben, können Sie mit dem Training des Modells beginnen. Wir führen es in zwei Schritten durch: Trainieren Sie zuerst nur unseren Klassifikator, dann das gesamte Modell.

 def evalModelstep1(typeModel): K.clear_session() gc.collect() model=createModel(typeModel) traiGen,testGen=getDatagen() model.fit_generator(generator=traiGen, epochs=4, steps_per_epoch=30000/16, validation_steps=len(testGen), validation_data=testGen, ) return model def evalModelstep2(model): early_stopping_callback = EarlyStopping(monitor='val_loss', patience=3) model.layers[0].trainable=True model.trainable=True model.compile(optimizer=keras.optimizers.Adam(lr=1e-5), loss='binary_crossentropy', metrics=['accuracy']) traiGen,testGen=getDatagen() model.fit_generator(generator=traiGen, epochs=25, steps_per_epoch=30000/16, validation_steps=len(testGen), validation_data=testGen, callbacks=[early_stopping_callback] ) return model def full_fit(): model_names=[ "xception", "resnet", "inception", "vgg16", "densenet121", "mobilenet" ] for model_name in model_names: print("#########################################") print("#########################################") print("#########################################") print(model_name) print("#########################################") print("#########################################") print("#########################################") model = evalModelstep1(model_name) model = evalModelstep2(model) model.save("~/data/models/model_new_"+str(model_name)+".h5") 

Rufen Sie full_fit () auf und warten Sie. Wir warten lange.

Als Ergebnis werden wir sechs trainierte Modelle haben. Wir werden die Genauigkeit dieser Modelle anhand eines separaten Teils der gekennzeichneten Daten überprüfen. Ich habe Folgendes erhalten:

Modellname


Genauigkeit%


Xception


87.3


Resnet


90,8


Inception


90.2


Vgg16


89.2


Densenet121


90.6


Mobilenet


86,5



Im Allgemeinen nicht viel, aber mit einer so kleinen Trainingsstichprobe kann man nicht mehr erwarten. Um die Genauigkeit leicht zu erhöhen, habe ich die Ausgaben der Modelle durch Mittelwertbildung kombiniert:

 def create_meta_model(): model_names=[ "xception", "resnet", "inception", "vgg16", "densenet121", "mobilenet" ] model_input = Input(shape=(244,244,3)) submodels=[] i=0; for model_name in model_names: filename= "~/data/models/model_new_"+str(model_name)+".h5" submodel = keras.models.load_model(filename) submodel.name = model_name+"_"+str(i) i+=1 submodels.append(submodel(model_input)) out=average(submodels) model = Model(inputs = model_input,outputs=out) model.compile(optimizer=keras.optimizers.Adam(lr=1e-4), loss='binary_crossentropy', metrics=['accuracy']) return model 

Die resultierende Genauigkeit betrug 91,3%. Aufgrund dieses Ergebnisses habe ich beschlossen, aufzuhören.

Klassifikator verwenden


Endlich ist der Klassifikator fertig und kann in Aktion gesetzt werden! Ich bereite die Eingabedaten vor und starte den Klassifikator - etwas mehr als einen Tag und 1,7 Millionen Fotos wurden verarbeitet. Jetzt ist der lustige Teil das Ergebnis. Bringen Sie sofort die ersten und letzten zehn Städte in die relative Anzahl der Straßen mit guter Abdeckung:



Vollständige Tabelle (anklickbares Bild)



Und hier ist die Straßenqualitätsbewertung nach Bundesfächern:



Voller Tisch


Bewertung nach Bundesbezirken:



Verteilung der Straßenqualität in Russland insgesamt:



Nun, das ist alles, jeder kann selbst Schlussfolgerungen ziehen.

Schließlich werde ich die besten Fotos in jeder Kategorie geben (die den Maximalwert in ihrer Klasse erhalten haben):

Bild



PS In den Kommentaren wurde zu Recht auf das Fehlen von Statistiken über die Jahre des Eingangs der Fotos hingewiesen. Ich korrigiere und gebe eine Tabelle:

Jahr


Anzahl der Fotos, Stk


200837
200913
2010157030
201160724
201242387
201312148

2014141021

201546143

2016410385

2017324279

2018581961

Source: https://habr.com/ru/post/de437542/


All Articles