def _remove_dynamic_fields(cls, paths, error_level=0):
        """Removes the dynamic embedded field(s) from the collection's schema.

        The actual data is **not** deleted from the collection.

        Args:
            paths: an iterable of ``embedded.field.names``
            error_level (0): the error level to use. Valid values are:

            -   0: raise error if a field cannot be removed
            -   1: log warning if a field cannot be removed
            -   2: ignore fields that cannot be removed
        """
        dataset = cls._dataset
        dataset_doc = dataset._doc

        paths = _remove_nested_paths(paths)

        del_paths = []

        for path in paths:
            field, is_default = cls._get_field(
                path, allow_missing=True, check_default=True
            )

            if field is None:
                fou.handle_error(
                    AttributeError(
                        "%s field '%s' does not exist"
                        % (cls._doc_name(), path)
                    ),
                    error_level,
                )
                continue

            if "." not in path:
                fou.handle_error(
                    ValueError(
                        "Cannot remove top-level %s field '%s' from schema"
                        % (cls._doc_name().lower(), path)
                    ),
                    error_level,
                )
                continue

            if is_default:
                fou.handle_error(
                    ValueError(
                        "Cannot remove default %s field '%s'"
                        % (cls._doc_name().lower(), path)
                    ),
                    error_level,
                )
                continue

            if field is not None and getattr(field, "read_only", False):
                fou.handle_error(
                    ValueError(
                        "Cannot remove read-only %s field '%s'"
                        % (cls._doc_name().lower(), path)
                    ),
                    error_level,
                )
                continue

            del_paths.append(path)

        if not del_paths:
            return

        # This fixes https://github.com/voxel51/fiftyone/issues/3185
        # @todo improve list field updates in general so this isn't necessary
        cls._reload_fields()

        for del_path in del_paths:
            cls._delete_field_schema(del_path)

        if cls._is_frames_doc:
            del_paths = [dataset._FRAMES_PREFIX + p for p in del_paths]

        dataset_doc.app_config._delete_paths(del_paths)
        dataset_doc.save()
Removes the dynamic embedded field(s) from the collection's schema.

        The actual data is **not** deleted from the collection.

        Args:
            paths: an iterable of ``embedded.field.names``
            error_level (0): the error level to use. Valid values are:

            -   0: raise error if a field cannot be removed
            -   1: log warning if a field cannot be removed
            -   2: ignore fields that cannot be removed
        
fiftyone/core/odm/mixins.py