Обучение на больших данных — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Порядок работы с большими данными)
(Трудности работы с большими данными)
Строка 54: Строка 54:
 
* В связи с большим потоком данных, конечный их набор постоянно изменяется, поэтому необходимо анализировать данные особым образом. Так, чтобы своевременно актуализировать информацию о них;
 
* В связи с большим потоком данных, конечный их набор постоянно изменяется, поэтому необходимо анализировать данные особым образом. Так, чтобы своевременно актуализировать информацию о них;
 
* При возникновении ошибок в модели приходится тратить очень много ресурсов на их поиск и устранение, так как объёмы данных очень велики;
 
* При возникновении ошибок в модели приходится тратить очень много ресурсов на их поиск и устранение, так как объёмы данных очень велики;
* Возникает проблема разнородности данных. Необходимо уметь обрабатывать данные различных форматов в рамках одной системы. Например, описания книг, фильмов и музыки.
+
* Возникает проблема разнородности данных. Необходимо уметь обрабатывать данные различных форматов в рамках одной системы. Например, описания книг, фильмов и музыки;
  
 
Также стоит отметить, что в связи с большой популярностью "больших данных", эта сфера очень быстро развивается, постоянно появляются всё новые технологии и инструменты для работы. Для бизнеса это приводит к дополнительным материальным затратам, т. к. крайне важно "идти в ногу со временем". Для специалистов по "большим данным" это так же приводит к дополнительным трудностям, т. к. необходимо крайне быстро овладевать этими новыми технологиями.
 
Также стоит отметить, что в связи с большой популярностью "больших данных", эта сфера очень быстро развивается, постоянно появляются всё новые технологии и инструменты для работы. Для бизнеса это приводит к дополнительным материальным затратам, т. к. крайне важно "идти в ногу со временем". Для специалистов по "большим данным" это так же приводит к дополнительным трудностям, т. к. необходимо крайне быстро овладевать этими новыми технологиями.

Версия 03:40, 21 января 2021

Эта статья находится в разработке!

Обучение на больших данных — раздел машинного обучения, специализирующийся на построении моделей, обрабатывающих большие объёмы данных, т. н. "big data" или "большие данные".

Понятие больших данных

Терминология и история

Сам термин "большие данные" часто трактуется очень неоднозначно, так как в ходе истории компьютерной техники объём данных и носителей этих данных возрастал в геометрической прогрессии. 50 лет назад жёсткий диск на 5 мегабайт нельзя было поднять без помощи автопогрузчика. В наши же дни маленькая коробочка весом в полкило может вмещать до нескольких терабайт данных (а то и десятков терабайт), а данные, хранящиеся на различных серверах можно исчислять петабайтами. Поэтому вопрос, какие же данные считать большими, довольно запутанный.

В качестве универсального решения было принято, что те данные, которые невозможно уместить на одном сервере, можно называть "большими". Но это далеко не все признаки "больших" данных. В наше время на серверных кластерах информация постоянно двигается, существует понятие "поток данных", генерируется много новой информации, и всё это постоянно записывается и перезаписывается. Из-за этого также возникает ряд проблем, но об этом позже.

Признаки больших данных. Правило VVV

Чтобы массив информации обозначить приставкой «big» он должен обладать следующими признаками[1]:

  • Volume (Объем) — данные измеряются по физической величине и занимаемому пространству на цифровом носителе. К «big» относят массивы свыше 150 Гб в сутки;
  • Velocity (Скорость, обновление) — информация регулярно обновляется и для обработки в реальном времени необходимы интеллектуальные технологии [2] в рамках больших данных;
  • Variety (Разнообразие) — информация в массивах может иметь неоднородные форматы, быть структурированной частично, полностью и скапливаться бессистемно. Например, социальные сети используют большие данные в виде текстов, видео, аудио, финансовых транзакций, картинок и прочего.

В современных системах рассматриваются два дополнительных фактора:

  • Variability (Изменчивость) — потоки данных могут иметь пики и спады, сезонности, периодичность. Всплески неструктурированной информации сложны в управлении, требует мощных технологий обработки;
  • Value (Значение данных) — информация может иметь разную сложность для восприятия и переработки, что затрудняет работу интеллектуальным системам. Например, массив сообщений из соцсетей — это один уровень данных, а транзакционные операции — другой. Задача машин определить степень важности поступающей информации, чтобы быстро структурировать.

Особенности работы с большими данными

Порядок работы с большими данными

Чтобы эффективно обрабатывать и анализировать большие данные, существуют такие инструменты как "аналитические модели"[3]. Их решения ищутся в замкнутом виде, в виде функциональных зависимостей. Такие модели способны строить гипотезы на основе больших данных, искать в них зависимости и закономерности – всю самую полезную для большинства бизнес-задач информацию. Кроме того, важна хорошая интерпретируемость построенной модели, так как это позволяет упростить её анализ без повторного её построения, что в условиях больших данных крайне важно. Для этого большие данные проходят через несколько этапов:

1. Чистка данных (англ. data cleaning) – поиск и исправление ошибок в первичном наборе информации, например, ошибки ручного ввода (опечатки), некорректные значения с измерительных приборов из-за кратковременных сбоев и т.д.;

2. Работа с признаками (англ. feature engineering) – генерация переменных для построения аналитических моделей;

3. Построение и обучение аналитической модели (англ. model selection) для предсказания целевой (таргетной) переменной. Так проверяются гипотезы о зависимости таргетной переменной от предикторов;

На практике это помогает решить множество задач. Например, проанализировать, как связаны отказы оборудования с условиями подачи напряжения, или определить вероятность своевременного возврата кредита частным заемщиком.

Методы обработки больших данных

К основным методам сбора и анализа больших данных относят следующие:

  • глубинный анализ или "добыча" данных (англ. data mining[4]) – обучение ассоциативным правилам, классификация, кластерный и регрессионный анализ;
  • краудсорсинг — категоризация и обогащение данных народными силами, т.е. с добровольной помощью сторонних лиц;
  • смешение и интеграция разнородных данных, таких как, цифровая обработка сигналов и обработка естественного языка;
  • машинное обучение, включая искусственные нейронные сети, сетевой анализ, методы оптимизации и генетические алгоритмы;
  • распознавание образов;
  • прогнозная аналитика;
  • имитационное моделирование;
  • пространственный и статистический анализ;
  • визуализация аналитических данных — рисунки, графики, диаграммы, таблицы.

Здесь ещё стоит отметить, что чаще всего любой процесс обработки больших данных является распределённым. Это достигается за счёт того, что большие данные практически всегда хранятся на кластерах серверов, а не на одном сервере. Каждый из них может производить вычисления и операции над данными. Например, задачу подсчёта минимального значения в больших данных можно распараллелить на те сервера, где эти данные хранятся, а затем уже их результаты сравнить и получить глобальный минимум. Этот процесс неплохо масштабируется, если говорить об объединении кластеров серверов в ещё большие кластеры.

Трудности работы с большими данными

При работе с большими данными важно помнить некоторые их особенности:

  • Данных очень много. Поэтому необходимо хранилище соответствующего размера, которое, как правило, является распределённым;
  • Любая попытка обработать большие данные целиком скорее всего приведёт к очень длительному ожиданию результата, если обработка происходит традиционными способами (например, чтение массива в цикле);
  • В связи с большим потоком данных, конечный их набор постоянно изменяется, поэтому необходимо анализировать данные особым образом. Так, чтобы своевременно актуализировать информацию о них;
  • При возникновении ошибок в модели приходится тратить очень много ресурсов на их поиск и устранение, так как объёмы данных очень велики;
  • Возникает проблема разнородности данных. Необходимо уметь обрабатывать данные различных форматов в рамках одной системы. Например, описания книг, фильмов и музыки;

Также стоит отметить, что в связи с большой популярностью "больших данных", эта сфера очень быстро развивается, постоянно появляются всё новые технологии и инструменты для работы. Для бизнеса это приводит к дополнительным материальным затратам, т. к. крайне важно "идти в ногу со временем". Для специалистов по "большим данным" это так же приводит к дополнительным трудностям, т. к. необходимо крайне быстро овладевать этими новыми технологиями.

Применение машинного обучения к большим данным

В условиях больших данных иногда возникает ситуация, когда пользователю нужно найти какие-то конкретные данные. Встаёт задача информационного поиска[5]. В силу большого объёма всех данных будет неэффективно перебирать их все в поисках того, что нужно. В данном случае можно применить алгоритмы машинного обучения, которые занимаются классификацией данных и их ранжированием. Поиск в размеченных и отсортированных данных происходит значительно быстрее. А в условиях больших данных это очень важно. Например, любая поисковая система при любом запросе должна давать результат за одно и то же время, однако объём тех данных, которые ей приходится проанализировать огромен, поэтому эффективный поиск в больших данных — очень важная задача, а машинное обучение сильно помогает в её решении.

Обработка разнородных данных в рамках одной системы

"Озеро" данных

При работе с большими данными часто возникает ситуация, когда одна и та же модель должна уметь обрабатывать данные различного формата. Это позволяет строить аналитические модели точнее и получать более достоверную информацию о данных в дальнейшем. Также отметим, что в данной ситуации данные берутся из множества различных источников, которые и определяют формат получаемых данных.

Рассмотрим модель распределенного хранения разнородных данных в концепции «озеро данных»[6].

[math]S=\langle\{DT\}_{i=1}^{n_{st}},\{SS\}_{j=1}^{m_{ss}},\{E\}_{k=1}^{p_e},IS,DS\rangle[/math]:
  • [math]\{DT\}_{i=1}^{n_{st}}[/math] — множество шаблонов данных;
  • [math]\{SS\}_{j=1}^{m_{ss}}[/math] — методы разбиения разнородных данных;
  • [math]\{E\}_{k=1}^{p_e}[/math] — множество исполнителей задач сбора данных;
  • [math]n_{st}[/math] — количество шаблонов данных;
  • [math]m_{ss}[/math] — количество типов данных;
  • [math]IS[/math] — метод индексирования данных в хранилище озера данных;
  • [math]DS[/math] — структура озера разнородных данных.

Данная модель позволяет хранить как сырые разнородные данные, так и структурированные данные в соответствии с предопределенной схемой. Такой результат достигается наличием шаблонов объектов и шаблонов параметров объектов. Также это позволяет снизить временные затраты на доступ к данным.

Рисунок 1: Модель хранения разнородных данных

Схема модели хранения разнородных данных

Рассмотрим схему модели, изображённую на рисунке 1:

  • Parameter template — шаблон параметра, хранимого в источнике данных;
  • Data source template — шаблон источника данных. Каждый источник данных может иметь множество параметров с различными типами данных [math]M_p[/math]. Структуру источника данных можно представить следующим образом: [math]ds=\langle p_1, p_2, \dots, p_k \rangle[/math];
  • Object template — шаблон некоторого объекта [math]O[/math]. Каждый объект может иметь множество источников данных [math]M_{ds}[/math]. Структуру объекта можно представить следующим образом: [math]O=\langle ds_1, ds_2, \dots, ds_m \rangle[/math].


Чтобы реализовать такую модель хранения, необходимо выполнить следующие действия:

1. Определение требуемой схемы данных:
Наблюдаемые объекты описываются набором гетерогенных данных. Схема данных для хранения такого рода данных представлена в соответствии с форматом: [math]sD=\langle gld, timestamp, (lat, lon), attrD \rangle[/math], в котором:
  • [math]gld[/math] — глобальный идентификатор объекта (уникален);
  • [math]timestamp[/math] — его временная метка;
  • [math](lat, lon)[/math] — координаты его местоположения на временной отметке;
  • [math]attrD[/math] — словарь, описывающий функции объекта и его значения.
2. Описание источников данных и настроек сборщиков данных:
Высокоуровневое описание произвольных источников данных определяется форматом: [math]sC=\langle sld, acs, (lat, lon), attrS \rangle[/math], в котором:
  • [math]sld[/math] — уникальный идентификатор источника данных;
  • [math]acs[/math] — список значений ключа для источника данных;
  • [math]attrS[/math] — внутренняя схема данных полученных от источника данных.
3. Построение схем привязки данных:
На этом этапе создается связь между исходной схемой источника данных и требуемой схемой. Эта ссылка представлена в виде набора [math]R[/math], содержащего пары атрибутов из набора [math]attrD[/math] схемы [math]sD[/math] и атрибутов из набора [math]attrS[/math] в схеме [math]sC[/math]:
[math]R=\{r_{i,j}\}[/math],
[math]r_{i,j}=\langle attrD_i, attrS_j \rangle[/math].
Здесь также следует помнить, что:
[math]\exists r_{lon}:\langle lon^{(sC)}, lon^{sD} \rangle[/math],
[math]\exists r_{lat}:\langle lat^{(sC)}, lat^{sD} \rangle[/math].
4. Реализация алгоритмов преобразования данных:
В соответствии с настройками привязки [math]R[/math] и алгоритмами [math]\{\alpha_k\}_{k=1}^{||R||}[/math] реализовано преобразование данных из исходной схемы в желаемую:
[math]\forall r_{i,j} \in R[/math],
[math]\exists \alpha_{i,j}:v(attrD_i) \implies v^*(attrS_j)[/math], где [math]v[/math] — значение атрибута.
5. Разделение данных:
Схема [math]DS[/math] для разделения потоков данных в микро-потоки:
[math]DS_{\alpha_k}=\langle df, \alpha_k, \{mdf_l\}_{l=1}^{L_{\alpha_k}}[/math].
Здесь [math]df[/math] — исходный поток данных, [math]mdf_l[/math][math]l[/math]-й l поток данных в памяти для определенного алгоритма [math]\alpha_k[/math], [math]L_{\alpha_k}[/math] – количество потоков. Данные разбиваются на потоки данных, подлежащие обработке в распределенной архитектуре, в соответствии с предопределенными задачами.
6. Вставка обработанных данных в базу данных:
Когда данные преобразуются в соответствии с определенной схемой, они вставляются в базу данных. Это позволяет извлекать разнородные данные из базы данных без дополнительных манипуляций с данными.

Данная модель позволяет обрабатывать массивы данных различных по структуре за счёт их преобразования к нужному формату. В дальнейшем аналитическая модель, работающая с этими данными сможет делать более точные прогнозы и гипотезы, так как по каждому объекту будет значительно больше различной информации.

Применение методов машинного обучения для построения "озера" данных

Представленная выше модель хорошо описывает схему хранения разнородных данных путём создания некоторого шаблона, который мог бы описывать все эти данные. Построение такого шаблона может быть очень трудоёмкой задачей, так как данных много и их форматов тоже может быть много. Здесь на помощь и приходит машинное обучение. Нейронные сети, при правильном их использовании, могут помочь в построении такого шаблона, обучаясь на разнородных данных. Такие сети были названы метапрофилировочные нейронные сети (англ. meta-profile neural network)[7].

Нейронная сеть, которую необходимо построить, состоит из нескольких других нейронных сетей. Каждая из них обрабатывает свой формат данных, следовательно выбор вида такой подсети стоит за разработчиком. В результате мы получаем некий новый объект, признаки которого будут зависеть от обработанных данных. Он и будет шаблоном объекта, который будет храниться в "озере" данных.

Так, например, можно анализировать предпочтения людей касательно жанров искусства, анализируя, какую музыку они слушают, какие книги читают и какие фильмы смотрят.

Работа с комплексом Apache Spark для обучения на больших данных

Об инструментах Apache Spark

Многие компании на сегодняшний день уже столкнулись с необходимостью обработки больших массивов данных. Для этой цели они начали использовать проекты экосистемы Apache Hadoop. Данная экосистема базируется на MapReduce, парадигме параллельного программирования, разработанного компанией Google. Основные достоинства MapReduce:

  • Масштабируемость;
  • Устойчивость к сбоям;
  • Простота использования.

Но при всех достоинствах данного инструмента, наблюдалась низкая производительность на итеративных алгоритмах (например, алгоритмы машинного обучения). Решение проблемы было найдено в университете Беркли: была разработана модель распределенных вычислений, которая имеет устойчивость к сбоям при пользовании распределенной коллекцией данных (англ. resilient distributed dataset, RDD). На основе RDD по сей день развивается система Apache Spark, которая обладает сравнительно высокой эффективностью при работе итеративных алгоритмов за счет кэширования результатов в памяти. На основе концепции распределенных коллекций разрабатываются распределенные системы:

  • Shark – хранилище данных;
  • GraphX – система обработки графовых данных;
  • Spark Streaming – система обработки потоковых данных;
  • Spark MLlib – библиотека алгоритмов машинного обучения.

Все из перечисленных систем совместимы со стеком технологий Hadoop. MLlib – основная библиотека Spark. Она предоставляет множество служебных программ, полезных для задач машинного обучения:

  • Классификация;
  • Регрессия;
  • Кластеризация;
  • Моделирование;
  • Сингулярное разложение и анализ по методу главных компонент;
  • Проверка гипотез и статистической выборки.

Примеры реализации алгоритмов с использованием Spark MLlib

Рассмотрим удобство использования Apache Spark на примере. Задача нашей модели предугадать захочет ли клиент оформить срочный вклад. Для этого воспользуемся данными из Machine Learning Repository. Напишем нашу модель на Python. Для начала работы с Apache Spark его необходимо установить, выполнив

pip install pyspark

Считаем данные из нашего файла и выведем информацию о датасете на экран:

from pyspark.sql import SparkSession
spark = SparkSession.builder.appName('ml-bank').getOrCreate()
df = spark.read.csv('bank.csv', header = True, inferSchema = True)
df.printSchema()

Результат:

root
 |-- age: integer (nullable = true)
 |-- job: string (nullable = true)
 |-- marital: string (nullable = true)
 |-- education: string (nullable = true)
 |-- default: string (nullable = true)
 |-- balance: integer (nullable = true)
 |-- housing: string (nullable = true)
 |-- loan: string (nullable = true)
 |-- contact: string (nullable = true)
 |-- day: integer (nullable = true)
 |-- month: string (nullable = true)
 |-- duration: integer (nullable = true)
 |-- campaign: integer (nullable = true)
 |-- pdays: integer (nullable = true)
 |-- previous: integer (nullable = true)
 |-- poutcome: string (nullable = true)
 |-- deposit: string (nullable = true)

Как видно наши данные состоят из множества столбцов, содержащих числа и строки Для большей информации выведем наши данные с помощью таблицы pandas. Для примера выведем 7 первых значений:

import pandas as pd
pd.DataFrame(df.take(7), columns=df.columns).transpose()

SparkMLFirstTable.png


Нас будут интересовать только численные данные. Для них построим таблицу с основной информацией (количество/ среднее по всей таблице/ среднеквадратичное отклонение / минимальное значение / максимальное значение):

numeric_features = [t[0] for t in df.dtypes if t[1] == 'int']
df.select(numeric_features).describe().toPandas().transpose()

SparkMLSecondTable.png

Оценим корреляцию между оставшимися данными:

from pandas.plotting import scatter_matrix
numeric_data = df.select(numeric_features).toPandas()
axs = scatter_matrix(numeric_data, figsize=(8, 8))
n = len(numeric_data.columns)
for i in range(n):
    v = axs[i, 0]
    v.yaxis.label.set_rotation(0)
    v.yaxis.label.set_ha('right')
    v.set_yticks(())
    h = axs[n-1, i] 
    h.xaxis.label.set_rotation(90)
    h.set_xticks(())

SparkMLThirdTable.png

На данных графиках можно увидеть зависимость, к примеру, между возрастом и балансом на карте. Не будем учитывать эти корреляции при построении наших моделей, однако избавимся от дня и месяца рождения, так как эти параметры не влияют на желание клиента оформить быстрый кредит.

df = df.select('age', 'job', 'marital', 'education', 'default', 'balance', 'housing', 'loan', 'contact', 'duration', 'campaign', 'pdays', 'previous', 'poutcome', 'deposit')
cols = df.columns

Подготовим оставшиеся данные для построения моделей.

from pyspark.ml.feature import StringIndexer, VectorAssembler, OneHotEncoder

categoricalColumns = ['job', 'marital', 'education', 'default', 'housing', 'loan', 'contact', 'poutcome']
stages = []
for categoricalCol in categoricalColumns:
    stringIndexer = StringIndexer(inputCol = categoricalCol, outputCol =   categoricalCol + 'Index')
    encoder = OneHotEncoder(inputCols=[stringIndexer.getOutputCol()], outputCols=[categoricalCol + "classVec"])
    stages += [stringIndexer, encoder]
label_stringIdx = StringIndexer(inputCol = 'deposit', outputCol = 'label')
stages += [label_stringIdx]
numericCols = ['age', 'balance', 'duration', 'campaign', 'pdays', 'previous']
assemblerInputs = [c + "classVec" for c in categoricalColumns] + numericCols
assembler = VectorAssembler(inputCols=assemblerInputs, outputCol="features")
stages += [assembler]

from pyspark.ml import Pipeline
pipeline = Pipeline(stages = stages)
pipelineModel = pipeline.fit(df)
df = pipelineModel.transform(df)
selectedCols = ['label', 'features'] + cols
df = df.select(selectedCols)
df.printSchema()

Наконец, поделим нашу выборку на обучающую и тестирующую

train, test = df.randomSplit([0.7, 0.3], seed = 2018)

Построим модели и выведем точность для:

Logistic Regression

from pyspark.ml.classification import LogisticRegression
lr = LogisticRegression(featuresCol = 'features', labelCol = 'label',    maxIter=10)
lrModel = lr.fit(train)

trainingSummary = lrModel.summary
print("Точность: " + str(trainingSummary.areaUnderROC))
Точность: 0.8865478305561797

Binary Classification

from pyspark.ml.evaluation import BinaryClassificationEvaluator
evaluator = BinaryClassificationEvaluator()
print("Точность: ", evaluator.evaluate(predictions))
Точность:  0.8837112925002687

Decision Tree

from pyspark.ml.classification import DecisionTreeClassifier
dt = DecisionTreeClassifier(featuresCol = 'features', labelCol = 'label',    maxDepth = 3)
dtModel = dt.fit(train)
predictions = dtModel.transform(test)
evaluator = BinaryClassificationEvaluator()
print("Точность: " + str(evaluator.evaluate(predictions, {evaluator.metricName: "areaUnderROC"})))
Точность: 0.7808118726917547

Random Forest

from pyspark.ml.classification import RandomForestClassifier
rf = RandomForestClassifier(featuresCol = 'features', labelCol = "green">'label'</font>)
rfModel = rf.fit(train)
predictions = rfModel.transform(test)
evaluator = BinaryClassificationEvaluator()
print("Точность: " + str(evaluator.evaluate(predictions, {evaluator.metricName: "areaUnderROC"})))
Точность: 0.8777131493473223

Gradient-Boosted Tree

from pyspark.ml.classification import GBTClassifier
gbt = GBTClassifier(maxIter=10)
gbtModel = gbt.fit(train)
predictions = gbtModel.transform(test)
evaluator = BinaryClassificationEvaluator()
print("Точность: " + str(evaluator.evaluate(predictions, {evaluator.metricName: "areaUnderROC"})))
Точность: 0.8935091626908479

Практическое применение Big Data

На сегодняшний день работа с большими данными популярна во многих рабочих сферах. Как правило, алгоритмы обрабатывают огромные потоки данных из различных источников, после чего создается предсказание следующего предполагаемого события. Можно выделить несколько областей, где использование больших данных набирает популярность:

  • Бизнес и Маркетинг. С помощью анализа последних произведенных транзакций алгоритмы с достаточно высокой точностью могут предсказать повышение спроса на определенный товар;
  • Социальные сети. У некоторых соцсетей уже есть встроенные алгоритмы анализа истории активности пользователей. Исходя из предпочтений пользователя и популярности некоторых сообществ, которые приближены к его интересам, создается предложение для конкретного человека вступить в сообщество, прослушать новую композицию;
  • Здравоохранение. Перебор симптомов болезней и эффектов лекарственных препаратов позволяет создавать новые средства по борьбе с новыми заболеваниями;
  • Предупреждение природных катастроф. Одна из важнейших сфер, где используются большие данные. Алгоритмы в этой сфере ежедневно обрабатывают огромные потоки данных в виде показаний датчиков с разных станций, чтобы приблизительно вычислить место и время предполагаемой катастрофы;
  • Правоохранительные органы. Даже небольшое повышение преступности в каком-либо регионе будет отслежено с помощью программ, изучающих статистику преступлений. Обработка больших массивов данных машиной позволяет быстрее реагировать и принимать соответствующие меры по предотвращению новых преступлений;
  • Сельское хозяйство. Фермерам доступны данные о погоде, состоянии почвы, влажности, созревании плодов, ходе роста и условиях для скота. Эта информация позволяет максимизировать и оптимизировать производство продукции под потребности рынка в реальном времени.

Однако внедрению Big Data мешает два фактора. Для мелких и средних компаний – это долгий и дорогой процесс сбора данных. А некоторая информация и вовсе относится к персональной – ее сбор без согласия гражданина запрещен.

См. также

Примечания

Источники информации