从文件和其他源加载数据
了解如何使用 API 将数据加载到 ML.NET 中进行处理和训练。 最初存储在文件或其他数据源(例如数据库、JSON、XML 或内存中集合)中的数据。
如果使用模型生成器,请参阅将训练数据加载到模型生成器。
创建数据模型
ML.NET 允许通过类定义数据模型。 例如,给定以下输入数据:
Size (Sq. ft.), HistoricalPrice1 ($), HistoricalPrice2 ($), HistoricalPrice3 ($), Current Price ($)
700, 100000, 3000000, 250000, 500000
1000, 600000, 400000, 650000, 700000
创建一个表示以下代码片段的数据模型:
public class HousingData
{
[LoadColumn(0)]
public float Size { get; set; }
[LoadColumn(1, 3)]
[VectorType(3)]
public float[] HistoricalPrices { get; set; }
[LoadColumn(4)]
[ColumnName("Label")]
public float CurrentPrice { get; set; }
}
使用列属性批注数据模型
特性为 ML.NET 提供有关数据模型和数据源的详细信息。
LoadColumn
特性指定属性的列索引。
重要
只有从文件加载数据时才需要 LoadColumn
。
将列加载为:
- 各个列,例如
HousingData
类中的Size
和CurrentPrices
。 - 以向量的形式一次加载多个列,例如
HousingData
类中的HistoricalPrices
。
如果有一个向量属性,请在数据模型中向该属性应用 VectorType
特性。 向量中的所有元素都必须是同一类型。 保持列与列之间的分隔状态可以提高特征工程的易用性和灵活性,但是对于大量的列,在单个列上操作会对训练速度产生影响。
ML.NET 通过列名称进行操作。 如果要将某个列的名称更改为该属性名称以外的其他名称,请使用 ColumnName
特性。 创建内存中对象时,仍然使用该属性名称创建对象。 但是,对于数据处理和生成机器学习模型,ML.NET 使用 ColumnName
特性中提供的值覆盖并引用该属性。
从单个文件加载数据
若要从文件加载数据,请使用 LoadFromTextFile
方法以及要加载的数据的数据模型。 由于 separatorChar
参数默认为制表符分隔,因此请根据需要为数据文件更改该参数。 如果文件有标头,请将 hasHeader
参数设置为 true
,以忽略文件中的第一行并开始从第二行加载数据。
//Create MLContext
MLContext mlContext = new MLContext();
//Load Data
IDataView data = mlContext.Data.LoadFromTextFile<HousingData>("my-data-file.csv", separatorChar: ',', hasHeader: true);
从多个文件加载数据
如果数据存储在多个文件中,只要数据架构相同,ML.NET 就允许从同一目录或多个目录中的多个文件加载数据。
从单个目录中的文件加载
当所有数据文件位于同一目录中时,请在 LoadFromTextFile
方法中使用通配符。
//Create MLContext
MLContext mlContext = new MLContext();
//Load Data File
IDataView data = mlContext.Data.LoadFromTextFile<HousingData>("Data/*", separatorChar: ',', hasHeader: true);
从多个目录中的文件加载
若要从多个目录加载数据,请使用 CreateTextLoader
方法创建 TextLoader
。 然后,使用 TextLoader.Load
方法并指定单个文件路径(不能使用通配符)。
//Create MLContext
MLContext mlContext = new MLContext();
// Create TextLoader
TextLoader textLoader = mlContext.Data.CreateTextLoader<HousingData>(separatorChar: ',', hasHeader: true);
// Load Data
IDataView data = textLoader.Load("DataFolder/SubFolder1/1.txt", "DataFolder/SubFolder2/1.txt");
从关系数据库加载数据
ML.NET 支持从各种关系数据库中加载数据(包括 SQL Server、Azure SQL 数据库、Oracle、SQLite、PostgreSQL、Progress、IBM DB2 等),这些关系数据库由 System.Data
提供支持。
注意
若要使用 DatabaseLoader
,请参考 System.Data.SqlClient NuGet 包。
指定具有名为 House
的表和以下架构的数据库:
CREATE TABLE [House] (
[HouseId] INT NOT NULL IDENTITY,
[Size] INT NOT NULL,
[NumBed] INT NOT NULL,
[Price] REAL NOT NULL
CONSTRAINT [PK_House] PRIMARY KEY ([HouseId])
);
数据可以通过 HouseData
等类进行建模:
public class HouseData
{
public float Size { get; set; }
public float NumBed { get; set; }
public float Price { get; set; }
}
然后,在应用程序中创建 DatabaseLoader
。
MLContext mlContext = new MLContext();
DatabaseLoader loader = mlContext.Data.CreateDatabaseLoader<HouseData>();
定义连接字符串以及要在数据库上执行的 SQL 命令,并创建 DatabaseSource
实例。 此示例使用具有文件路径的 LocalDB SQL Server 数据库。 但是,DatabaseLoader 支持本地和云中数据库的任何其他有效连接字符串。
重要
Microsoft 建议使用最安全的可用身份验证流。 如果要连接到 Azure SQL,建议使用 Azure 资源的托管标识这种身份验证方法。
string connectionString = @"Data Source=(LocalDB)\MSSQLLocalDB;AttachDbFilename=<YOUR-DB-FILEPATH>;Database=<YOUR-DB-NAME>;Integrated Security=True;Connect Timeout=30";
string sqlCommand = "SELECT CAST(Size as REAL) as Size, CAST(NumBed as REAL) as NumBed, Price FROM House";
DatabaseSource dbSource = new DatabaseSource(SqlClientFactory.Instance, connectionString, sqlCommand);
类型不是 Real
的数值数据必须转换为 Real
。 Real
类型表示为单精度浮动点值或 Single
,这是 ML.NET 算法应采用的输入类型。 在此示例中,Size
和 NumBed
列是数据库中的整数。 使用 CAST
内置函数,将其转换为 Real
。 由于 Price
属性的类型已经是 Real
,所以它按原样加载。
使用 Load
方法将数据加载到 IDataView
。
IDataView data = loader.Load(dbSource);
加载图像
若要从目录中加载图像数据,请先创建一个包含图像路径和标签的模型。 ImagePath
是图像在数据源目录中的绝对路径。 Label
是实际图像文件的类或类别。
public class ImageData
{
[LoadColumn(0)]
public string ImagePath;
[LoadColumn(1)]
public string Label;
}
public static IEnumerable<ImageData> LoadImagesFromDirectory(string folder,
bool useFolderNameAsLabel = true)
{
string[] files = Directory.GetFiles(folder, "*", searchOption: SearchOption.AllDirectories);
foreach (string file in files)
{
if (Path.GetExtension(file) != ".jpg")
continue;
string label = Path.GetFileName(file);
if (useFolderNameAsLabel)
label = Directory.GetParent(file).Name;
else
{
for (int index = 0; index < label.Length; index++)
{
if (!char.IsLetter(label[index]))
{
label = label.Substring(0, index);
break;
}
}
}
yield return new ImageData()
{
ImagePath = file,
Label = label
};
}
}
然后加载图像:
IEnumerable<ImageData> images = LoadImagesFromDirectory(
folder: "your-image-directory-path",
useFolderNameAsLabel: true
);
若要从目录中加载内存中原始图像,请创建一个模型来保存原始图像字节数组和标签:
public class InMemoryImageData
{
[LoadColumn(0)]
public byte[] Image;
[LoadColumn(1)]
public string Label;
}
static IEnumerable<InMemoryImageData> LoadInMemoryImagesFromDirectory(
string folder,
bool useFolderNameAsLabel = true
)
{
string[] files = Directory.GetFiles(folder, "*",
searchOption: SearchOption.AllDirectories);
foreach (string file in files)
{
if (Path.GetExtension(file) != ".jpg")
continue;
string label = Path.GetFileName(file);
if (useFolderNameAsLabel)
label = Directory.GetParent(file).Name;
else
{
for (int index = 0; index < label.Length; index++)
{
if (!char.IsLetter(label[index]))
{
label = label.Substring(0, index);
break;
}
}
}
yield return new InMemoryImageData()
{
Image = File.ReadAllBytes(file),
Label = label
};
}
}
从其他源加载数据
除了加载存储在文件中的数据外,ML.NET 还支持从各种源加载数据,这些源包括:
- 内存中集合
- JSON/XML
在使用流式处理源时,ML.NET 预计输入采用内存中集合的形式。 因此,在使用 JSON/XML 等源时,请确保将数据格式化为内存中集合。
给定以下内存中集合:
HousingData[] inMemoryCollection = new HousingData[]
{
new HousingData
{
Size =700f,
HistoricalPrices = new float[]
{
100000f, 3000000f, 250000f
},
CurrentPrice = 500000f
},
new HousingData
{
Size =1000f,
HistoricalPrices = new float[]
{
600000f, 400000f, 650000f
},
CurrentPrice=700000f
}
};
使用 LoadFromEnumerable
方法将内存中集合加载到 IDataView
中:
重要
LoadFromEnumerable
假定其所加载的 IEnumerable
是线程安全的。
// Create MLContext
MLContext mlContext = new MLContext();
//Load Data
IDataView data = mlContext.Data.LoadFromEnumerable<HousingData>(inMemoryCollection);