如何在Google Earth Engine中实现k折交叉验证及精度评估
Google Earth Engine中土地覆盖分类的k折交叉验证实现
实现要点
- 使用固定随机种子确保每次运行的训练/测试拆分完全一致
- 将样本划分为k个互斥折叠,循环用k-1个折叠训练模型,剩余1个折叠测试
- 收集每折的精度指标,最终计算所有折叠的平均精度
修改后的完整代码
var roi = ee.Geometry.Polygon( [[[30.68, -29.54], [30.72, -29.54], [30.72, -29.41], [30.68, -29.41], [30.68, -29.54]]]); var period_of_interest = ee.Filter.date('2022-01-01', '2022-02-28'); /** * Function to mask clouds using the Sentinel-2 QA band * @param {ee.Image} image Sentinel-2 image * @return {ee.Image} cloud masked Sentinel-2 image */ function maskS2clouds(image) { var qa = image.select('QA60'); // Bits 10 and 11 are clouds and cirrus, respectively. var cloudBitMask = 1 << 10; var cirrusBitMask = 1 << 11; // Both flags should be set to zero, indicating clear conditions. var mask = qa.bitwiseAnd(cloudBitMask).eq(0) .and(qa.bitwiseAnd(cirrusBitMask).eq(0)); return image.updateMask(mask).divide(10000); } //Build the Sentinel 2 collection, filtered by date, bounds and percentage of cloud cover var imageCollection = ee.ImageCollection('COPERNICUS/S2_SR') .filter(period_of_interest) .filterBounds(roi) .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE',10)) .map(function(image){return image.clip(roi)}) .map(maskS2clouds) //1 NDVI (Normalized Difference Vegetation Index) var imageCollection = imageCollection.map(function(image) { var NDVI = image.expression( "(NIR - RED) / (NIR + RED)", { RED: image.select("B4"), // RED NIR: image.select("B8"), // NIR }).rename('NDVI') return image.addBands(NDVI) }) //2 gNDVI (Green Normalized Difference Vegetation Index) var imageCollection = imageCollection.map(function(image) { var gNDVI = image.expression( "(NIR - Green)/(NIR + Green)", { NIR: image.select("B8"), Green: image.select("B3"), }).rename('gNDVI') return image.addBands(gNDVI) }) //3 NDVIre (Normalized Difference Vegetation Index Red-edge) var imageCollection = imageCollection.map(function(image) { var NDVIre = image.expression( "(NIR - Rededge1) / (NIR + Rededge1)", { NIR: image.select("B8"), Rededge1: image.select("B5"), }).rename('NDVIre') return image.addBands(NDVIre) }); //4 EVI (Enhanced Vegetation Index) var imageCollection = imageCollection.map(function(image) { var EVI = image.expression( '2.5 * ((NIR - RED) / (NIR + 6 * RED - 7.5 * BLUE + 1))', { 'NIR': image.select('B8'), 'RED': image.select('B4'), 'BLUE': image.select('B2') }).rename('EVI') return image.addBands(EVI) }) //5 SAVI (Soil Adjusted Vegetation Index) var imageCollection = imageCollection.map(function(image) { var SAVI = image.expression( "((NIRnarrow - RED) / (NIRnarrow + RED + 0.5) * 1.5)", { NIRnarrow: image.select("B8A"), RED: image.select("B4") }).rename('SAVI') return image.addBands(SAVI) }); //6 SR-BlueRededge1 (Simple Blue and Red-edge 1 Ratio) var imageCollection = imageCollection.map(function(image) { var SRBlueRededge1 = image.expression( "(BLUE / REDedge1)", { BLUE: image.select("B2"), REDedge1: image.select("B5") }).rename('SRBlueRededge1') return image.addBands(SRBlueRededge1) }); //7 SR-NIRnarrowRededge1 (Simple NIR and Red-edge 1 Ratio) var imageCollection = imageCollection.map(function(image) { var SRNIRnarrowRededge1 = image.expression( "(NIRnarrow / REDedge1)", { NIRnarrow: image.select("B8A"), REDedge1: image.select("B5") }).rename('SRNIRnarrowRededge1') return image.addBands(SRNIRnarrowRededge1) }); //8 Pigment Specific Simple Ratio (chlorophyll index) var imageCollection = imageCollection.map(function(image) { var PSSRa = image.expression( "(NIR / RED)", { NIR: image.select("B8"), RED: image.select("B4") }).rename('PSSRa') return image.addBands(PSSRa) }); //9 Chlogreen (Chlorophyll Green Index) var imageCollection = imageCollection.map(function(image) { var GCI = image.expression( "((NIR/Green))-1", { NIR: image.select("B8"), Green: image.select("B3"), }).rename('GCI') return image.addBands(GCI) }); //10 BAI var imageCollection = imageCollection.map(function(image) { var BAI = image.expression( "(Blue - NIR)/(Blue + NIR)", { Blue: image.select("B2"), NIR: image.select("B8"), }).rename('BAI') return image.addBands(BAI) }); //11 Lcaroc (Leaf Carotenoid Content) var imageCollection = imageCollection.map(function(image) { var Lcaroc = image.expression( "(Rededge3) / (Blue - Rededge1)", { Rededge3: image.select("B7"), Blue: image.select("B2"), Rededge1: image.select("B5"), }).rename('Lcaroc') return image.addBands(Lcaroc) }); //12 Lchloc (Leaf Chlorophyll Content) var imageCollection = imageCollection.map(function(image) { var Lchloc = image.expression( "(Rededge3) / (Rededge1)", { Rededge3: image.select("B7"), Rededge1: image.select("B5"), }).rename('Lchloc') return image.addBands(Lchloc) }); //13 LAnthoC (Leaf Anthocynanid Content) var imageCollection = imageCollection.map(function(image) { var Lanthoc = image.expression( "(Rededge3) / (Green + Rededge1)", { Rededge3: image.select("B7"), Green: image.select("B3"), Rededge1: image.select("B5"), }).rename('Lanthoc') return image.addBands(Lanthoc) }); //14 Mcari (Modified Chlorophyll Absorption in Reflectance Index) var imageCollection = imageCollection.map(function(image) { var Mcari = image.expression( "((Rededge1-Red)-0.2*(Rededge1-Green))*(Rededge1/Red)", { Red: image.select("B4"), Green: image.select("B3"), Rededge1: image.select("B5"), }).rename('Mcari') return image.addBands(Mcari) }); //15 PSRI (Plant Senescence Reflectance Index) var imageCollection = imageCollection.map(function(image) { var PSRI = image.expression( "(Red-Green)/Rededge2", { Red: image.select("B4"), Green: image.select("B3"), Rededge2: image.select("B6"), }).rename('PSRI') return image.addBands(PSRI) }); //16 PPR (Plant Pigment Ratio) var imageCollection = imageCollection.map(function(image) { var PPR = image.expression( "(Green-Blue)/(Green+Blue)", { Green: image.select("B3"), Blue: image.select("B2"), }).rename('PPR') return image.addBands(PPR) }); //17 PVR (Photosyntetic Vigour Ratio) var imageCollection = imageCollection.map(function(image) { var PVR = image.expression( "(Green-Red)/(Green+Red)", { Green: image.select("B3"), Red: image.select("B4"), }).rename('PVR') return image.addBands(PVR) }); var image = imageCollection.median() print("Final_image", image); // Create training data var LULC = ee.FeatureCollection('users/shaedengokool/points') Map.addLayer(LULC.style({color: 'black', fillColor: '00000000'}), false) print (LULC) var bands = ['B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B8A', 'B11', 'B12', 'NDVI', 'gNDVI','NDVIre', 'EVI', 'SAVI', 'SRBlueRededge1', "SRNIRnarrowRededge1", 'PSSRa', 'GCI', 'BAI', 'Lcaroc', 'Lchloc', 'Lanthoc', 'Mcari', 'PSRI', 'PPR', 'PVR'] var training_data = image.select(bands).sampleRegions({ collection: LULC, properties: ['Id'], geometries: true, scale: 10.00, tileScale: 16, }); print(training_data, "training_data") // -------------------------- k折交叉验证核心代码 -------------------------- var k = 5; // 设置折叠数,这里用5折 var seed = 1; // 固定随机种子,保证每次拆分一致 // 给每个样本分配折叠编号 var withFold = training_data.randomColumn('random', seed) .map(function(feature) { // 将0-1的随机数映射到0-(k-1)的折叠编号 var fold = ee.Number(feature.get('random')).multiply(k).floor(); return feature.set('fold', fold); }); // 生成折叠索引列表 var folds = ee.List.sequence(0, k-1); // 定义每折的训练与评估逻辑 var kFoldResults = folds.map(function(foldNum) { foldNum = ee.Number(foldNum); // 拆分训练集(排除当前折叠)和测试集(当前折叠) var trainingSet = withFold.filter(ee.Filter.neq('fold', foldNum)); var testSet = withFold.filter(ee.Filter.eq('fold', foldNum)); // 训练梯度提升树分类器 var classifier = ee.Classifier.smileGradientTreeBoost(300).train({ features: trainingSet, classProperty: 'Id', inputProperties: bands }); // 对测试集进行分类并计算混淆矩阵 var validated = testSet.classify(classifier); var confMatrix = validated.errorMatrix('Id', 'classification'); // 返回当前折叠的所有精度指标 return ee.Dictionary({ 'fold': foldNum, 'confusionMatrix': confMatrix, 'overallAccuracy': confMatrix.accuracy(), 'kappa': confMatrix.kappa(), 'producersAccuracy': confMatrix.producersAccuracy(), 'consumersAccuracy': confMatrix.consumersAccuracy() }); }); // 提取所有折叠的精度指标并计算平均值 var accuracyList = ee.List(kFoldResults); var oaList = accuracyList.map(function(item) { return ee.Dictionary(item).get('overallAccuracy'); }); var kappaList = accuracyList.map(function(item) { return ee.Dictionary(item).get('kappa'); }); var meanOA = ee.Number(oaList.reduce(ee.Reducer.mean())); var meanKappa = ee.Number(kappaList.reduce(ee.Reducer.mean())); // 打印结果 print('各折叠详细精度结果', kFoldResults); print('平均总体精度', meanOA); print('平均Kappa系数', meanKappa); // 可选:用全部样本训练最终分类器并生成分类图 var finalClassifier = ee.Classifier.smileGradientTreeBoost(300).train({ features: training_data, classProperty: 'Id', inputProperties: bands }); var classified = image.select(bands).classify(finalClassifier); Map.addLayer(classified, {min: 1, max: 5, palette: ['#FF0000','#00FF00','#0000FF','#FFFF00','#FF00FF']}, '分类结果');
关键部分说明
固定随机种子
在randomColumn方法中传入固定的seed参数(这里设为1),确保每次运行时样本的随机数生成完全一致,从而保证折叠划分的可重复性。折叠划分逻辑
通过将0-1的随机数乘以k后取整,把样本均匀分配到k个折叠中,每个折叠的样本量大致相等。循环训练与评估
利用ee.List.map遍历每个折叠,每次用k-1个折叠的样本训练模型,剩余1个折叠作为测试集,计算该折的混淆矩阵和精度指标。平均精度计算
提取所有折叠的总体精度和Kappa系数,用ee.Reducer.mean()计算平均值,得到模型的综合精度评估结果。
内容的提问来源于stack exchange,提问作者Shaeden Gokool
相关产品推荐
相关产品推荐

