Hacemos una calificación de las ciudades rusas por la calidad de la carretera.



Una vez más, conduciendo un automóvil alrededor de mi ciudad natal y yendo alrededor de otro pozo, pensé: ¿existían carreteras tan "buenas" en todas partes de nuestro país y decidí que deberíamos evaluar objetivamente la situación con la calidad de las carreteras en nuestro país?

Formalización de tareas


En Rusia, los requisitos para la calidad de las carreteras se describen en GOST R 50597-2017 “Carreteras y carreteras. Requisitos para el estado operativo aceptable bajo las condiciones de garantizar la seguridad vial. Métodos de control ". Este documento define los requisitos para cubrir la calzada, los bordes de las carreteras, las franjas divisorias, las aceras, las pasarelas peatonales, etc., y también establece los tipos de daños.

Dado que la tarea de determinar todos los parámetros de las carreteras es bastante extensa, decidí reducirla y centrarme solo en el problema de determinar defectos en la cobertura de la carretera. En GOST R 50597-2017, se distinguen los siguientes defectos en el revestimiento de la carretera:

  • baches
  • rompe
  • reducciones
  • turnos
  • peines
  • rastrear
  • carpeta de sudoración

Decidí abordar estos defectos.

Recolección de datos


¿Dónde puedo obtener fotografías que muestren secciones suficientemente grandes de la carretera, e incluso con referencia a la geolocalización? La respuesta vino en pedrería: panoramas en los mapas de Yandex (o Google), sin embargo, después de un poco de búsqueda, encontré varias opciones alternativas más:

  • emitir motores de búsqueda de imágenes para solicitudes relevantes;
  • fotos en sitios para recibir quejas (Rosyama, ciudadano enojado, Virtud, etc.)
  • Opendatascience impulsó un proyecto para detectar defectos en la carretera con un conjunto de datos marcado - github.com/sekilab/RoadDamageDetector

Desafortunadamente, un análisis de estas opciones mostró que no son muy adecuadas para mí: emitir motores de búsqueda tiene mucho ruido (muchas fotos que no son carreteras, varios renders, etc.), las fotos de los sitios para recibir quejas contienen solo fotos con grandes violaciones de la superficie de asfalto , hay bastantes fotos con pequeñas violaciones de cobertura y sin violaciones en estos sitios, el conjunto de datos del proyecto RoadDamageDetector se recopila en Japón y no contiene muestras con grandes violaciones de cobertura, así como carreteras sin cobertura en absoluto.

Como las opciones alternativas no son adecuadas, utilizaremos panoramas de Yandex (excluí la opción de panorama de Google, ya que el servicio se presenta en menos ciudades de Rusia y se actualiza con menos frecuencia). Decidió recopilar datos en ciudades con una población de más de 100 mil personas, así como en centros federales. Hice una lista de nombres de ciudades: había 176 de ellos, más tarde resulta que solo 149 de ellos tienen panorámicas. No profundizaré en las características del análisis de mosaicos, diré que al final obtuve 149 carpetas (una para cada ciudad) en las que había un total de 1.7 millones de fotos. Por ejemplo, para Novokuznetsk, la carpeta se veía así:



Por el número de fotos descargadas, las ciudades se distribuyeron de la siguiente manera:

Mesa
Ciudad
Número de fotos, piezas
Moscú

86048

San petersburgo

41376

Saransk

18880

Podolsk

18560

Krasnogorsk

18208

Lyubertsy

17760

Kaliningrado

16928

Kolomna

16832

Mytishchi

16192

Vladivostok

16096

Balashikha

15968

Petrozavodsk

15968

Ekaterinburg

15808

Veliky Novgorod

15744

Naberezhnye Chelny

15680

Krasnodar

15520

Nizhny Novgorod

15488

Khimki

15296

Tula

15296

Novosibirsk

15264

Tver

15200

Miass

15104

Ivanovo

15072

Vologda

15008

Zhukovsky

14976

Kostroma

14912

Samara

14880

Korolev

14784

Kaluga

14720

Cherepovets

14720

Sebastopol

14688

Pushkino

14528

Yaroslavl

14464

Ulyanovsk

14400

Rostov del Don

14368

Domodedovo

14304

Kamensk-Uralsky

14208

Pskov

14144

Yoshkar-Ola

14080

Kerch

14080

Murmansk

13920

Togliatti

13920

Vladimir

13792

Águila

13792

Syktyvkar

13728

Dolgoprudny

13696

Khanty-Mansiysk

13664

Kazán

13600

Engels

13440

Arkhangelsk

13280

Bryansk

13216

Omsk

13120

Syzran

13088

Krasnoyarsk

13056

Shchelkovo

12928

Penza

12864

Cheliábinsk

12768

Cheboksary

12768

Nizhny Tagil

12672

Stavropol

12672

Ramenskoye

12640

Irkutsk

12608

Angarsk

12608

Tyumen

12512

Odintsovo

12512

Ufa

12512

Magadán

12512

Perm

12448

Kirov

12256

Nizhnekamsk

12224

Makhachkala

12096

Nizhnevartovsk

11936

Kursk

11904

Sochi

11872

Tambov

11840

Pyatigorsk

11808

Volgodonsk

11712

Riazán

11680

Saratov

11616

Dzerzhinsk

11456

Orenburg

11456

Montículo

11424

Volgogrado

11264

Izhevsk

11168

Crisóstomo

11136

Lipetsk

11072

Kislovodsk

11072

Surgut

11040

Magnitogorsk

10912

Smolensk

10784

Khabarovsk

10752

Kopeysk

10688

Maykop

10656

Petropavlovsk-Kamchatsky

10624

Taganrog

10560

Barnaul

10528

Sergiev Posad

10368

Elista

10304

Sterlitamak

9920

Simferopol

9824

Tomsk

9760

Orekhovo-Zuevo

9728

Astrakhan

9664

Evpatoria

9568

Noginsk

9344

Chita

9216

Belgorod

9120

Biysk

8928

Rybinsk

8896

Severodvinsk

8832

Vorónezh

8768

Blagoveshchensk

8672

Novorossiysk

8608

Ulan-Ude

8576

Serpukhov

8320

Komsomolsk-on-Amur

8192

Abakán

8128

Norilsk

8096

Yuzhno-Sakhalinsk

8032

Obninsk

7904

Essentuki

7712

Bataysk

7648

Volzhsky

7584

Novocherkassk

7488

Berdsk

7456

Arzamas

7424

Pervouralsk

7392

Kemerovo

7104

Elektrostal

6720

Derbent

6592

Yakutsk

6528

Murom

6240

Nefteyugansk

5792

Reutov

5696

Birobidzhan

5440

Novokuybyshevsk

5248

Salekhard

5184

Novokuznetsk

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



Preparando un conjunto de datos para el entrenamiento


Y entonces, el conjunto de datos está ensamblado, ¿cómo ahora, al tener una foto de la sección de la carretera y los objetos que lo rodean, descubrir la calidad del asfalto que se muestra en él? Decidí cortar una parte de la foto que mide 350 * 244 píxeles en el centro de la foto original, justo debajo del centro. Luego reduzca la pieza cortada horizontalmente a un tamaño de 244 píxeles. La imagen resultante (tamaño 244 * 244) será la entrada para el codificador convolucional:



Para comprender mejor con qué datos trato, las primeras 2000 fotos que marqué yo mismo, el resto de las fotos fueron marcadas por empleados de Yandex.Tolki. Ante ellos planteé una pregunta con la siguiente redacción.

Indique qué superficie de la carretera ve en la foto:

  1. Suelo / Escombros
  2. Adoquines, tejas, pavimento
  3. Rieles, vías férreas
  4. Agua, grandes charcos
  5. Asfalto
  6. No hay camino en la foto / Objetos extraños / La cobertura no es visible debido a los automóviles

Si el artista eligió "Asphalt", aparecerá un menú que ofrece evaluar su calidad:

  1. Excelente cobertura
  2. Grietas simples leves / baches simples poco profundos
  3. Grietas grandes / grietas de rejilla / baches menores simples
  4. Grandes baches / baches profundos / revestimiento destruido

Como mostraron las ejecuciones de prueba de las tareas, los ejecutantes de Y. Toloki no difieren en la integridad del trabajo: accidentalmente hacen clic en los campos con el mouse y consideran la tarea completada. Tuve que agregar preguntas de control (en la tarea había 46 fotografías, 12 de las cuales eran de control) y permitir la aceptación tardía. Como preguntas de control, utilicé esas imágenes que marqué yo mismo. Automaticé la aceptación tardía: Y. Toloka le permite cargar los resultados del trabajo en un archivo CSV y cargar los resultados de la verificación de las respuestas. La verificación de las respuestas funcionó de la siguiente manera: si la tarea contiene más del 5% de respuestas incorrectas para controlar las preguntas, entonces se considera incompleta. Además, si el contratista indicó una respuesta que es lógicamente cercana a verdadera, entonces su respuesta se considera correcta.
Como resultado, obtuve unas 30 mil fotos etiquetadas, que decidí distribuir en tres clases para capacitación:

  • "Bueno": fotos etiquetadas como "Asfalto: revestimiento excelente" y "Asfalto: grietas simples menores"
  • "Medio" - fotos con la etiqueta "Adoquines, azulejos, pavimento", "rieles, vías de ferrocarril" y "Asfalto: grietas grandes / grietas en la rejilla / baches menores"
  • “Grande”: fotos etiquetadas como “Suelo / piedra triturada”, “Agua, charcos grandes” y “Asfalto: una gran cantidad de baches / baches profundos / pavimento destruido”
  • Fotos etiquetadas "No hay camino en la foto / Objetos extraños / La cobertura no es visible debido a los automóviles" había muy pocos (22 piezas) y los excluí de trabajos posteriores

Desarrollo y formación de clasificadores.


Entonces, los datos se recopilan y etiquetan, procedemos al desarrollo del clasificador. Por lo general, para las tareas de clasificación de imágenes, especialmente cuando se entrena en conjuntos de datos pequeños, se usa un codificador convolucional listo para usar, a cuya salida se conecta un nuevo clasificador. Decidí usar un clasificador simple sin una capa oculta, una capa de entrada de tamaño 128 y una capa de salida de tamaño 3. Decidí usar varias opciones preparadas formadas en ImageNet como codificadores:

  • Xception
  • Resnet
  • Inicio
  • Vgg16
  • Densenet121
  • Mobilenet

Aquí está la función que crea el modelo Keras con el codificador dado:

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 

Para el entrenamiento, utilicé un generador con aumento (dado que las capacidades del aumento incorporado en Keras me parecían insuficientes, utilicé la biblioteca Augmentor ):

  • Pendientes
  • Distorsión aleatoria
  • Vueltas
  • Cambio de color
  • Turnos
  • Cambiar contraste y brillo
  • Agregar ruido aleatorio
  • Recortar

Después del aumento, las fotos se veían así:



Código generador:

 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) 

El código muestra que el aumento no se usa para los datos de prueba.

Con un generador sintonizado, puede comenzar a entrenar el modelo, lo llevaremos a cabo en dos etapas: primero, solo entrene nuestro clasificador, luego todo el modelo.

 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") 

Llame a full_fit () y espere. Estamos esperando por mucho tiempo.

Como resultado, tendremos seis modelos entrenados, verificaremos la precisión de estos modelos en una parte separada de los datos etiquetados; Recibí lo siguiente:

Nombre del modelo


Precisión%


Xception


87,3


Resnet


90,8


Inicio


90,2


Vgg16


89,2


Densenet121


90,6


Mobilenet


86,5



En general, no mucho, pero con una muestra de entrenamiento tan pequeña, uno no puede esperar más. Para aumentar ligeramente la precisión, combiné las salidas de los modelos promediando:

 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 

La precisión resultante fue del 91,3%. En este resultado, decidí parar.

Usando clasificador


Finalmente, el clasificador está listo y se puede poner en acción. Preparo los datos de entrada y ejecuto el clasificador: se ha procesado un poco más de un día y 1,7 millones de fotos. Ahora la parte divertida son los resultados. Inmediatamente traiga las primeras y últimas diez ciudades en el número relativo de carreteras con buena cobertura:



Mesa completa (imagen en la que se puede hacer clic)



Y aquí está la calificación de la calidad del camino por temas federales:



Mesa completa


Calificación por distritos federales:



Distribución de la calidad de la carretera en Rusia en su conjunto:



Bueno, eso es todo, todos pueden sacar conclusiones por sí mismos.

Finalmente, daré las mejores fotos en cada categoría (que recibió el valor máximo en su clase):

Imagen



PD: En los comentarios, con bastante razón señaló la falta de estadísticas sobre los años de recepción de las fotografías. Corrijo y doy una tabla:

Año


Número de fotos, piezas


200837
200913
2010157030
201160724
201242387
201312148

2014141021

201546143

2016410385

2017324279

2018581961

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


All Articles